Model emits cwd-relative file links for files it edited OUTSIDE cwd (git worktree sessions). In full worktrees these resolve silently to a stale copy at a different commit.
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?
When a session's working directory is a git worktree but the project's CLAUDE.md
directs all work to the main checkout outside it, Claude emits markdown file links
as paths relative to cwd (per the harness's link-formatting rule). Those links point
into the worktree, not at the files it actually edited.
Two failure modes, different severity:
Mode 1 - sparse worktree (holds only a subset of tracked files)
Link 404s. Annoying, but self-evident to the user.
Mode 2 - FULL worktree (holds ~every tracked file, at the worktree branch's
older commit) <-- NOT REPORTED
The link RESOLVES, to a stale copy of the same-named file at a different
commit. It looks like it worked. The user reads content that is not what
Claude was describing. Line-numbered links (file.py:378) are worse again,
because line numbers drift between commits.
Mode 2 is the one worth fixing. Every existing file-link issue is about links that
visibly fail; none is about a link that quietly opens the wrong version.
NOT A DUPLICATE OF #78733 / #87918, though the symptom overlaps.
#78733: session enters a worktree MID-SESSION via EnterWorktree; the model emits
correct worktree-relative links; the UI wrongly resolves them against the launch
directory. Model right, UI wrong.
This report: the session is LAUNCHED in a worktree and works outside it. The model
emits cwd-relative links for files that are not under cwd. The UI resolves against
cwd, correctly. Model wrong, UI right.
They are mirror images. FIXING #78733 DOES NOT FIX THIS - it entrenches it, because
#78733 asks the UI to resolve consistently against the session's live cwd, which is
exactly the base under which these links fail. #78733's workaround (prefix with
.claude/worktrees/<name>/) is also unavailable here: the target is not under the
worktree at any prefix.
Also distinct from #81817 (absolute-path hrefs silently dropped on Windows desktop),
which is client-side resolution of a given href; this is the model's CHOICE of href.
They compose badly - see "What Should Happen".
Evidence that this is systematic: 10 prior sessions across 8 distinct worktrees over
3.5 weeks all did it, plus a negative control session whose cwd was the work
directory and whose links were correct. Detail in the Logs field.
What Should Happen?
A file link should open the file Claude actually read or wrote.
Suggested fixes. Note fix (1) is INSUFFICIENT ALONE on Windows desktop:
- Amend the instruction: use a cwd-relative path only when the file is under
cwd; otherwise emit the absolute path.
NOT SUFFICIENT ALONE. Per #81817, absolute paths used as markdown-link
hrefs are silently dropped on Windows desktop, so this trades a dead relative
link for a dead absolute one. It only helps if #81817 is fixed too.
- Client-side validation: warn, or rewrite to absolute/file://, when a linked
path does not exist under cwd.
THE ONLY FIX THAT CATCHES MODE 2. In mode 2 the path DOES exist under
cwd - it is simply the wrong copy - so detecting it requires the client to
compare against where the file was actually read or written this session.
- Instruct the model to emit a BACKTICKED PLAIN PATH rather than a markdown link
when the target is outside cwd. Per #81817, backticked absolute paths
auto-linkify and work on Windows desktop, so this is effective today without
waiting on #81817.
- In worktree sessions, surface in the environment block that edits are landing
outside the worktree.
Recommended combination: (3) now, (2) properly.
Error Messages/Logs
THERE IS NO ERROR MESSAGE. That is the defect. In mode 1 the click does nothing;
in mode 2 it opens a file successfully - just the wrong version. Nothing in the
tool results tells the model its links are broken: Write/Edit accept absolute paths
and report success, and no tool result surfaces that cwd differs from the edit
location. Claude cannot see the failure; only the user can.
(That also makes asking other sessions useless as a diagnostic - a session that did
this all day would sincerely report that it had not. The evidence below came from
transcript search, not self-report.)
DIAGNOSTIC STATE, session of 2026-08-28:
cwd : D:\Viking\leaky-kd\.claude\worktrees\next-session-prompt-6408af
branch claude/next-session-prompt-6408af, HEAD 57178fba2, 38 files
work : D:\Viking\leaky-kd
branch Split_dl_SGD_for_teacher, HEAD d99a3218f, 29,377 tracked files
file in worktree? in main checkout?
dataset/leaky.py NO yes
dataset/tinyimagenet.py NO yes
VIKING_ACCESS.md NO yes
CODE_INDEX.md NO yes
misc/capture_per_run_membership.py NO yes
CONFIRMED MODE-2 INSTANCE (a different session):
Session "MTA 4", cwd = worktrees/dazzling-lederberg-019e4d, emitted
[dataset/leaky.py:378](dataset/leaky.py:378)
That worktree is a FULL checkout - 28,685 files - at HEAD 89815e469, while the
main checkout is at d99a3218f. The link resolved, to a different commit's copy of
leaky.py, at a line number that need not correspond.
All three worktrees still on disk are full checkouts, i.e. the silent-corruption
case is the common one here, not the rare one:
next-session-prompt-5767b6 29,356 files
next-session-prompt-8db228 28,758 files
dazzling-lederberg-019e4d 28,685 files
TEN PRIOR SESSIONS, found by full-text search of local session transcripts for the
substrings "](misc/" and "](dataset/leaky.py)". This is a LOWER BOUND, not an
exhaustive count.
session cwd date
---------------------------------------------------------------------------
MTA 13 worktrees/next-session-prompt-5767b6 2026-08-26
MTA 12 worktrees/next-session-prompt-8db228 2026-08-25
MTA 4 worktrees/dazzling-lederberg-019e4d 2026-08-24
MTA 11 worktrees/next-session-prompt-20bd3a 2026-08-24
MTA 10 worktrees/memorization-transfer-audit-6159c9 2026-08-21
MTA 9 worktrees/next-session-prompt-8db228 2026-08-20
MTA 7 worktrees/upbeat-cartwright-f2f34c 2026-08-16
MTA 5 worktrees/memorization-transfer-audit-6159c9 2026-08-15
MTA 6 worktrees/amazing-elion-21956f 2026-08-14
MTA 3 worktrees/mta-writeup-blog-dd436a 2026-08-04
---------------------------------------------------------------------------
Process Performance D:\Viking\leaky-kd (NOT a worktree) 2026-06-10
^ negative control: the only session with cwd == the work directory, and the
only one whose links were correct.
Every worktree session did it. The behaviour tracks cwd, not the session.
Steps to Reproduce
- Create a git worktree under <repo>/.claude/worktrees/<name>, on a branch behind
the main checkout. Either a sparse worktree (mode 1) or a full one (mode 2).
- Put an instruction in <repo>/CLAUDE.md that all work must be done in <repo>
itself, not in the worktree. (Common when the worktree lacks the data/outputs
the task needs.)
- Launch a Claude Code session with cwd = the worktree.
- Ask Claude to edit <repo>/dataset/leaky.py, then reference that file in its reply.
- Claude emits [dataset/leaky.py](dataset/leaky.py).
- Click it.
Sparse worktree -> nothing happens; the file is not there.
Full worktree -> the file OPENS, from the worktree branch's older commit.
Diff it against <repo>/dataset/leaky.py to see they differ.
Platform observed: Windows desktop app (Claude 1.37937.1.0). Step 6's mode-2
behaviour is the important one.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
Claude Code version: 2.1.246 Host: Claude desktop app 1.37937.1.0 (Windows, WindowsApps package) Platform: Windows 11 Home 10.0.26200 Installation: desktop app only — no CLI on PATH, no npm global install, no ~/.claude/local
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
Other
Additional Information
ENVIRONMENT
Claude Code 2.1.246
Host Claude desktop app 1.37937.1.0 (Windows, WindowsApps package)
Platform Windows 11 Home 10.0.26200
Installation desktop app only - no CLI on PATH, no npm global install, no
~/.claude/local. (Hence GitHub rather than /bug: the slash
command is CLI-only and unavailable to desktop-app users.)
WHY THIS CONFIGURATION ARISES - please don't read it as user error
The obvious triage response is "just work inside the worktree". That is not
available here, and the reason generalises.
The worktree contains only git-tracked files. This project's actual work needs
things that are gitignored by design: ~29,000 training-output files, SQLite
queue databases, model checkpoints, and pulled cluster logs. None of that
exists in a worktree. So the project's CLAUDE.md mandates that all data work is
done in the main checkout, and every session launched into a worktree is
therefore working outside its own cwd for the whole session.
Any repo whose working set includes large gitignored artefacts - ML training
output, build caches, datasets, local databases - lands in exactly this shape.
It is not an unusual setup, and the 10-session evidence table suggests it is
the normal one for this user.
HOW TO MEASURE PREVALENCE YOURSELVES
The 10-session table was produced by full-text search over local session
transcripts for the substrings "](misc/" and "](dataset/leaky.py)", then
comparing each hit's recorded cwd against the file's real location. The same
approach generalises: search transcripts for markdown links whose href is a
relative path, and flag any session where cwd is under .claude/worktrees/ while
the linked path does not exist beneath it. That should give a population-level
rate without needing user reports - which matters, because users only notice
mode 1. Mode 2 produces no complaint at all: the file opens and looks fine.
SEVERITY FRAMING
Mode 1 is a UX annoyance. Mode 2 is a correctness problem: in a tool used for
code review and analysis, a link that silently opens a different commit's copy
of the file being discussed can send the user to the wrong code while the
conversation describes the right code. Line-numbered links compound it.
PROVENANCE
Diagnosed in-session by the assistant after the user reported that links never
worked; the assistant had no way to detect it unaided, which is the point of
the "no error message" field. Filesystem state, git HEADs and the transcript
search were all run and verified in that session.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗