[BUG] Desktop 2.1.217+: hook subprocesses spawn with unreadable cwd — getcwd EPERM kills all git; WorktreeCreate hook always fails, worktree sessions abort
Summary
Since Desktop 2.1.217, hook subprocesses (and the file-index worker) are spawned with a working directory the child process cannot read. getcwd() fails with EPERM, which makes every git command die at startup (fatal: Unable to read current working directory: Operation not permitted). With a WorktreeCreate hook configured, this means worktree session creation always fails (worktree_hook_failed, session aborted) — the hook can never run git, and per the hook contract the app never falls back to creating the worktree itself.
Environment
- Claude Code Desktop 2.1.217 → 2.1.219 (regression first observed seconds after first 2.1.217 launch; 2.1.219 still affected)
- macOS (Darwin 25.5.0), repo located under
~/Desktop/...(TCC-protected folder) WorktreeCreate/WorktreeRemovehooks configured in project.claude/settings.json, pointing at a repo script that runs git
What happens
Creating a worktree session (paths lightly redacted):
2026-07-30 15:08:34 [info] Creating worktree for session local_c70e3fe5-... from ~/Desktop/repo
2026-07-30 15:08:34 [error] WorktreeCreate hook failed: "$CLAUDE_PROJECT_DIR/scripts/worktree.sh" hook-create: fatal: Unable to read current working directory: Operation not permitted
fatal: Unable to read current working directory: Operation not permitted
set git config user.name to name a branch
2026-07-30 15:08:34 [error] Worktree creation failed with error: worktree_hook_failed, aborting session { baseRepo: '~/Desktop/repo', sessionId: 'local_c70e3fe5-...' }
The app's own file-index worker hits the identical error on every launch:
2026-07-30 15:08:15 [warn] [file-index-worker] git ls-files failed; falling back to shallow BFS (deep paths may be missing in @-mention autocomplete): fatal: Unable to read current working directory: Operation not permitted
Why this is the spawn environment, not the hook script
- The identical hook command succeeds when run from a terminal (payload piped on stdin): creates the worktree, prints the path.
- In the failing run, the hook script itself executed fine — its own stderr output appears in the log (a message the script emits from a function several calls in). So the child could read and execute files under the repo by absolute path; only
getcwd()failed. - The app's own file-index worker fails with the byte-identical git error, so the broken spawn environment is shared app infrastructure, not the user's hook.
errno signature pins the cause
Reproduced both broken-cwd modes locally:
- cwd deleted →
fatal: Unable to read current working directory: No such file or directory(ENOENT) - cwd inside a sandbox-denied path (
sandbox-execprofile denyingfile-read*on the cwd subtree, repo still readable) →fatal: Unable to read current working directory: Operation not permitted(EPERM) — byte-identical to the observed failure, including the "script runs, git dies" behavior.
So the hook child inherits a cwd that its sandbox/TCC context cannot resolve, while the project directory itself is readable.
Regression timeline (from ~/Library/Logs/Claude/main*.log)
- First occurrence ever: 2026-07-23 09:36:39, i.e. 3 seconds after
[CCD] Initialized with version 2.1.217(09:36:36). Zero occurrences in all older logs. - 584 occurrences in main2.log (2.1.217/2.1.219 era), 23 more today; persists across app relaunches.
- Notably, the app's main-process worktree creation (the built-in flow used when no WorktreeCreate hook exists) still worked on 2.1.219 (2026-07-24) — only specific child-process classes (hook exec, file-index worker) get the unreadable cwd.
Impact
- With any
WorktreeCreatehook that touches git: cannot create worktree sessions at all (session aborts). - For everyone on macOS with a repo in a TCC-protected folder: file-index worker silently degrades to shallow BFS → deep paths missing from
@-mention autocomplete.
Workaround (for other users hitting this)
Top of the hook script, before any git call:
# App-spawned hooks can inherit a cwd the sandbox can't read (getcwd EPERM),
# which kills every git command. Re-anchor on the script's own repo.
pwd -P >/dev/null 2>&1 || cd "$SCRIPT_DIR/.."
Verified to rescue the hook under the sandbox repro. Limits: it cannot help if the child's sandbox also denies writes/stat on the repo's ancestors (unverifiable from outside the app), and nothing user-side can fix the file-index worker.
Suggested fix
Spawn hook subprocesses (and the file-index worker) with cwd set to the project directory — which is what the hooks documentation already states — or at minimum a directory the child's sandbox profile can resolve, rather than inheriting whatever cwd the spawning helper had.
Related
Likely the same root cause as #81310 (file-index-worker vs TCC-protected ~/Documents) and possibly #82666 (intermittent EPERM on TCC-protected ~/Desktop, also names the file-index worker). This report adds the hook-subprocess surface (hard failure of worktree creation), the exact version pin (2.1.217), and the ENOENT/EPERM differential that identifies the mechanism.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗