[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)

Status Open
Reported on v2.1.205
Maintainer reply None cached
Activity 3 comments · opened Jul 18, 2026

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

  1. Open the desktop app on a git repo (main checkout = project root).
  2. Agent runs EnterWorktree → creates/enters .claude/worktrees/<name>; session cwd switches in (env header confirms).
  3. Agent writes a file in the worktree and emits a markdown link relative to its cwd (e.g. .review/report.md).
  4. 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.)
  5. 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" />

View original on GitHub ↗

3 Comments

calsmith1-nokia · 1 month ago

Hitting what looks like the same root defect, just surfacing through a different feature: worktree sessions resolving .mcp.json env-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:

  • Main checkout has its own .env pointing at deployment A.
  • A worktree of the same repo has its own separate .env (loaded via direnv) pointing at deployment B.
  • .mcp.json (tracked in git, identical in both): "url": "${MY_APP_DEPLOYMENT_URL}/mcp".

Repro:

  1. Confirm via direnv exec . env that the worktree's shell has the correct env (deployment B).
  2. Launch Claude Code from the worktree with that env forced (direnv exec . claude).
  3. Confirm via ps eww -p <pid> that the actual claude process's exec-time environment has the correct value (deployment B) — ruling out shell/direnv timing issues.
  4. Run /mcp inside 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 .env value), causing a connection failure since deployment A is a different/inaccessible host.

Supporting evidence:

  • ~/.claude.json's projects map had no entry keyed by the worktree's path at all — only an entry for the main checkout path, which also carried an activeWorktreeSession key. Worktree sessions appear to be tracked/configured entirely under the main project's identity rather than their own path.
  • The main checkout's .env file contained the exact stale values that kept appearing in the worktree's /mcp failures, byte-for-byte.
  • Editing the main checkout's .env (not the worktree's, which was already correct) fixed /mcp resolution in the worktree session.
  • Ruled out along the way: direnv hook timing, stale daemon/background-worker processes, stale shell snapshots — all confirmed correct at every layer up to and including the actual claude process'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.

calebdelnay · 1 month ago

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 via EnterWorktree path:, e.g. after git worktree add ../repo-feature), every file card fails. The worktree isn't under the project root at all:

  • launch dir: ~/Projects/acme/app
  • worktree: ~/Projects/acme/app-feature-x

Claude confirmed this in the shipped renderer for Claude Desktop (citing file resources/ion-dist/assets/v1/cd2efac6e-C6Wbl1p6.js):

yn = gn?.originCwd,
wn = (e,t,n) => n && t?.sessionFolders ? [e, ...t.sessionFolders.map(...)] : [e],
Mn = kn && !ae(bn, wn),   // outsideRoot

The root list is built from originCwd (the launch directory), not the session's live cwd.

The symptom for me is the missing_or_outside branch, not the generic one quoted in the OP:

n === "missing" ? (t ? "missing_or_outside" : "missing")
//                 ^ outsideRoot

→ "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 inside originCwd.

CLI 2.1.250, Desktop 1.40609.1, Linux.

sudodo · 5 days ago

+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> (not EnterWorktree); 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 as research_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 PostToolUse hook (matcher EnterWorktree|Bash) that, once per session when the hook input cwd is under .claude/worktrees/, returns additionalContext telling the model to write hrefs as absolute paths or with the .claude/worktrees/<name>/ prefix. Zero cost outside worktree sessions.