[Bug] Worktree cwd guard permanently blocks Bash mid-session while MCP servers retain filesystem access
Bug Description
Title: Bash becomes permanently unusable mid-session in a --worktree session; cwd guard rejects every command while MCP servers still read
the same tree
---
Summary
Partway through a session launched with --worktree, every Bash call began failing pre-execution with the cwd-resolution guard, and every
Read under the project tree began returning EPERM. Neither recovered for the remainder of the session, and neither is recoverable from
inside the session (the guard also rejects the cd that would fix it).
The filesystem was never the problem: an MCP server process kept reading the same tree successfully throughout. This is Claude Code's
tool-sandbox view, not macOS TCC and not file permissions.
Environment
- Launched with: claude --worktree
- Session cwd: /Users/<user>/Documents/dev/doit/<repo>/.claude/worktrees/pr615-review
- Repo: private Go monorepo; the worktree is a normal git worktree
- Platform: darwin 25.6.0, arm64 · Shell: zsh
- Model: claude-opus-5[1m]
- Session ID: 2e90ec43-e3c3-4b4a-9bd2-3a6d760c8f1d
Timeline
- Session launched with --worktree; cwd set by Claude Code to <repo>/.claude/worktrees/pr615-review.
- The first ~8 Bash calls succeeded from that exact cwd: gh pr view, git fetch, git checkout, git log, git diff --stat, go build ./..., go
vet, go test, go list -deps, plus several grep/sed calls. Two of them changed directory — one cd iac/modules/synapse-env, one cd
<absolute worktree path> — and both worked.
- From that point on, every Bash invocation returned the guard error before executing — including pwd, true, and printf 'ok\n'.
- Simultaneously, Read on any absolute path under /Users/<user>/Documents/ returned EPERM — including the main checkout, not just the
worktree (<repo>/CLAUDE.md, <repo>/go.mod, <repo>/internal/memorystore/usage.go).
- Read and Write on /Users/<user>/.claude/ and /private/tmp/claude-501/ kept working normally for the rest of the session.
- dangerouslyDisableSandbox: true did not bypass it.
- Still failing ~12 consecutive attempts later, spread across several turns and many minutes.
Verbatim error
This command was blocked because its working directory is spelled in a form that cannot be
safely resolved (for example through a symlink storing a raw dot segment, a network-share or
device-namespace shape, or an unreadable ancestor directory). If the directory is inside the
worktree /Users/<user>/Documents/dev/doit/<repo>/.claude/worktrees/pr615-review, re-run the
command from its direct symlink-free path.
Key discriminator: the filesystem was fine
The aide MCP server — a separate process, not mediated by the tool sandbox — kept reading the same tree after Read/Bash had started failing
on it:
- instance_info returned db_path: <repo>/.aide/memory/memory.db, socket_path: <repo>/.aide/aide.sock, daemon.reachable: true
- code_stats returned 589 files / 610 symbols / 35,285 references, read out of that DB
Both paths are inside the tree that Read reported as EPERM. That rules out the OS, TCC, and file permissions.
Second data point: the context-mode MCP server's sandboxed executor could cd into the worktree, and pwd -P returned the path unchanged — no
symlink anywhere in the resolution. That rules out the "symlink storing a raw dot segment" branch that the error message leads with. (It saw
the directory as empty, consistent with its own separate sandbox having no repo mount — unrelated.)
Impact
The session could no longer run any command or read any project file. An in-progress PR review had to be completed from data already in
context, could not be verified (tests, terraform validate), and could not be published (gh unreachable). There is no in-session recovery:
the cwd cannot be changed from inside because the guard rejects that command too.
Two issues worth separating
- The guard rejects a cwd that Claude Code itself chose. --worktree and EnterWorktree always create the worktree under .claude/worktrees/,
and the guard lists a raw dot segment among its rejection causes. There is no setting to relocate it — worktree.baseRef controls the base
ref (fresh/head), not the path. A user following the documented flow cannot avoid the shape.
- Whatever made the ancestor unreadable to the sandbox mid-session. The path shape alone is not the trigger, since ~8 calls succeeded from
that same cwd first. Something in the sandbox's view changed between tool calls.
Suggested fixes
- Don't hard-fail on a cwd the product created — resolve to the real path and proceed.
- Report which of the three causes actually fired. The message lists three, the leading one was provably not the cause here, and that sent
diagnosis down a false path (macOS TCC /…
Note: Content was truncated.