[BUG] Desktop app: clickable file links intermittently fail after EnterWorktree mid-session — UI resolves links against the original project root, not the live worktree cwd (file-link variant of #45202)
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)
- [x] I am using the latest version of Claude Code
What's Wrong?
In the Claude Code desktop app, EnterWorktree mid-session correctly moves the agent's tool cwd into the worktree (.claude/worktrees/<name>), and the env header reports the session's primary working directory as the worktree. But the desktop app UI resolves clickable markdown file links against the original project root (the main checkout the app launched with), not the live session cwd.
This manifests intermittently: when the agent emits a path relative to the project root (including the .claude/worktrees/<name>/ prefix) the link opens; when it emits a path relative to the worktree cwd (e.g. .review/report.md) — a legitimate reading, since the env reports cwd as the worktree — the UI can't resolve it. Using "Copy relative path" on a working vs broken link confirms one includes .claude/worktrees/<name>/ and the other doesn't.
Same root cause as #45202 (VS Code button, closed as stale): the UI base directory doesn't follow a mid-session worktree switch.
What Should Happen?
After a mid-session worktree switch, the UI should resolve file paths/links against the session's live cwd (the worktree) — matching what EnterWorktree advertises ("switches the session's working directory"). Then worktree-relative links open correctly and the base is consistent for both the UI and the agent.
Error Messages/Logs
Couldn't read this file. It may have been deleted or moved, or it lives outside the working directory.
Steps to Reproduce
- Open the desktop app on a git repo (main checkout = project root).
- Agent runs EnterWorktree → creates/enters .claude/worktrees/<name>; session cwd switches in (env header confirms).
- Agent writes a file in the worktree and emits a markdown link relative to its cwd (e.g. .review/report.md).
- Click it → "Couldn't read this file … outside the working directory." (File exists at .claude/worktrees/<name>/.review/report.md relative to the project root.)
- Intermittency check: when the agent instead emits .claude/worktrees/<name>/.review/report.md (project-root-relative), the same file opens fine.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.205 (Claude Code)
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
Other
Additional Information
Related: #45202 — "Desktop app: VS Code button opens original directory after creating worktree mid-session." Same root cause (UI base directory doesn't follow a mid-session worktree switch), different surface (VS Code button, Linux). That issue was auto-closed as stale by a bot (not fixed, not a deliberate decline).
Platform detail: Windows 11, build 10.0.26200, Claude Code desktop app.
<img width="685" height="978" alt="Image" src="https://github.com/user-attachments/assets/db543634-c1fb-4e86-a77c-59fa9faf311a" />
3 Comments
Hitting what looks like the same root defect, just surfacing through a different feature: worktree sessions resolving
.mcp.jsonenv-var substitution (${MY_APP_DEPLOYMENT_URL}) against the main checkout's environment/.env, not the worktree's own — mirroring this issue's "resolves against the original project root, not the live worktree cwd."Setup:
.envpointing at deployment A..env(loaded via direnv) pointing at deployment B..mcp.json(tracked in git, identical in both):"url": "${MY_APP_DEPLOYMENT_URL}/mcp".Repro:
direnv exec . envthat the worktree's shell has the correct env (deployment B).direnv exec . claude).ps eww -p <pid>that the actualclaudeprocess's exec-time environment has the correct value (deployment B) — ruling out shell/direnv timing issues./mcpinside that session.Expected: MCP server connects using deployment B (the worktree's own configured value).
Actual: MCP server still tries to connect to deployment A (the main checkout's
.envvalue), causing a connection failure since deployment A is a different/inaccessible host.Supporting evidence:
~/.claude.json'sprojectsmap had no entry keyed by the worktree's path at all — only an entry for the main checkout path, which also carried anactiveWorktreeSessionkey. Worktree sessions appear to be tracked/configured entirely under the main project's identity rather than their own path..envfile contained the exact stale values that kept appearing in the worktree's/mcpfailures, byte-for-byte..env(not the worktree's, which was already correct) fixed/mcpresolution in the worktree session.claudeprocess's exec-time environment.This suggests the underlying bug isn't specific to file-link resolution (as filed here) or to MCP substitution (what I hit) — it's that worktree sessions inherit project identity/config from the main checkout rather than their own directory, and that surfaces wherever code reads "the project's env/config" instead of "this session's live cwd/environment." Happy to share a full (sanitized) session transcript if useful for repro.
Platform: macOS, Claude Code CLI.
Running into this myself which I think might be the same root cause.
When the worktree is a sibling of the launch directory rather than nested under
.claude/worktrees/(entered viaEnterWorktree path:, e.g. aftergit worktree add ../repo-feature), every file card fails. The worktree isn't under the project root at all:~/Projects/acme/app~/Projects/acme/app-feature-xClaude confirmed this in the shipped renderer for Claude Desktop (citing file
resources/ion-dist/assets/v1/cd2efac6e-C6Wbl1p6.js):The root list is built from
originCwd(the launch directory), not the session's live cwd.The symptom for me is the
missing_or_outsidebranch, not thegenericone quoted in the OP:→ "Couldn't find this file — It may have been deleted, or it lives outside the working directory."
The read is missing because it's scoped to the wrong root. The file is present on disk and the card prints its correct absolute path.
Nested
.claude/worktrees/worktrees only pass containment by virtue of sitting insideoriginCwd.CLI 2.1.250, Desktop 1.40609.1, Linux.
+1, reproduced on macOS Claude Desktop (Code tab) with Claude Code 2.1.284.
Variant: the worktree was created by the assistant with
git worktree add .claude/worktrees/<name> -b <branch>(notEnterWorktree); the harness switched the agent's primary working directory to the worktree, but the file pane kept resolving links against the original session folder. A link written asresearch_scheme/x.md(relative to the worktree cwd, as the built-in instruction asks) showed "Couldn't find this file". The same file linked as.claude/worktrees/<name>/research_scheme/x.md(relative to the repo root) opened fine, confirming the workaround above.Interim workaround we use: a
PostToolUsehook (matcherEnterWorktree|Bash) that, once per session when the hook inputcwdis under.claude/worktrees/, returnsadditionalContexttelling the model to write hrefs as absolute paths or with the.claude/worktrees/<name>/prefix. Zero cost outside worktree sessions.