[BUG] Worktree isolation guard blocks Edit/Write through a symlink the worktree setup itself created to share config with the main checkout
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [ ] I am using the latest version of Claude Code (running 2.1.234; latest on npm is 2.1.278 — not reproduced there before filing, see Additional Information)
What's Wrong?
Edit/Write is refused for a path inside an isolated worktree when that path is a symlink the worktree's own setup created on purpose, pointing at a file in the main checkout. The refusal is:
This session is isolated in the worktree <path>. Edit the worktree copy of this file instead of the shared-checkout path.
There is no separate "worktree copy" of the file. It's a symlink by design, so the suggested remedy doesn't exist. The block fires identically whether the edit targets the worktree-side symlink path or the symlink's real target path in the main checkout.
What Should Happen?
A worktree setup should be able to intentionally share specific gitignored files (config, docs, etc.) with the main checkout via a symlink placed inside the worktree, and have Edit/Write honor that. Either the guard should treat a path that is itself inside the worktree root as writable even when its realpath resolves outside (i.e. not silently canonicalize through a symlink the worktree itself owns), or there should be a supported way to declare specific paths/symlinks as intentionally shared and writable from an isolated session.
Steps to Reproduce
- In a git repo, add a worktree:
git worktree add ../repo-wt some-branch - Inside the worktree, symlink a gitignored shared file back to the main checkout, e.g.:
ln -s /path/to/main-checkout/SHARED.md ../repo-wt/SHARED.md
(This is the pattern a worktree bootstrap script uses to share gitignored project config across worktrees instead of leaving it missing in each one.)
EnterWorktree(path: "../repo-wt")Edit(orWrite)SHARED.mdfrom inside the worktree (via the symlink path).- Observe the denial quoted above.
- Retry the same edit addressed at the symlink's real target path in the main checkout instead.
- Observe the identical denial, even though this is the file's actual designed location, not an arbitrary escape.
Claude Model
Not sure / Multiple models — this is harness/tool-level behavior, not model-dependent.
Is this a regression?
I don't know
Claude Code Version
2.1.234 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Other (Claude Code VS Code native extension, not the integrated terminal — no closer option in the list)
Additional Information
- Related: #95776 reports the identical denial string for a different setup (a second, fully-registered git worktree misclassified as "the shared checkout," not a symlink). Filing separately since the mechanism and the requested fix differ, flagging it here so maintainers can judge overlap.
- Checked the
EnterWorktree/ExitWorktreetool schemas directly: neither exposes a parameter for declaring a path or symlink writable-through from an isolated session. The only workaround found:ExitWorktree(action: "keep"), edit from the main checkout, then re-enter. - Verified on disk (macOS): these are real symlinks (
lrwxr-xr-x), created by a worktree bootstrap script, present identically across multiple independent worktrees in the same repo — a deliberate, repeatable pattern, not a one-off misconfiguration. - "Anthropic API" above is inferred from the absence of AWS Bedrock / Google Vertex environment markers in this session, not from a directly surfaced setting.