EnterWorktree: a stale/empty worktree directory silently resolves to the parent repo, so "isolated" commits land on the parent's checked-out branch

Status Fixed / completed
Reported on v2.1.214
Maintainer reply None cached
Activity 1 comment · opened Jul 19, 2026 · closed Aug 17, 2026

Summary

If a directory under .claude/worktrees/ exists but has no .git entry — a worktree that was pruned, partially removed, or never fully created — git does not error there. It walks up and resolves to the parent repository. An agent that called EnterWorktree, believes it is isolated, and commits will write to the parent's checked-out branch, which is commonly main.

There is no error, no warning, and exit code 0. The failure is invisible at the point of observation: running git status inside the stale directory reports the parent's branch and dirty files, which reads as a perfectly healthy worktree.

Reproduction

mkdir /tmp/wtbug && cd /tmp/wtbug
git init -q . && git commit -q --allow-empty -m "init"
mkdir -p .claude/worktrees/agent-stale
cd .claude/worktrees/agent-stale

git rev-parse --show-toplevel     # -> /tmp/wtbug   (the PARENT, not the worktree)
git branch --show-current          # -> master

echo pwned > newfile.txt && git add -A && git commit -m "work done in my isolated worktree"
echo $?                            # -> 0

cd /tmp/wtbug
git log -1 --oneline               # -> the commit, on master
git ls-files                       # -> .claude/worktrees/agent-stale/newfile.txt

The commit lands on the parent's branch, and the "isolated" file is now tracked in the parent repo at the worktree path.

Why this matters

Worktree isolation exists so an agent's work cannot reach the shared checkout. This failure mode produces exactly the outcome isolation is meant to prevent, and produces it silently. In a background job the operator sees a successful commit and no indication anything was wrong.

It is reachable in normal use: stale directories accumulate when worktree cleanup does not complete ("auto-cleaned if unchanged" does not always hold), and worktree paths are also manipulated by hand via git worktree add/remove/prune.

Observed in practice

A repository was found with six directories under .claude/worktrees/, zero of them registered in git worktree list, one of which had no .git entry at all. Separately, another repo had two registered worktrees whose directories no longer existed — drift runs in both directions.

Suggested fixes

Any one of these would close it:

  1. EnterWorktree validates the target before returning success — assert git rev-parse --show-toplevel inside the directory equals the directory itself, and fail loudly if it resolves elsewhere.
  2. Guard the commit path — if the working directory is under a .claude/worktrees/ path but resolves to a toplevel outside it, refuse and explain.
  3. Leave a marker file in managed worktrees and treat its absence as a hard error rather than falling through to parent resolution.

(1) alone would cover the tool-driven path; (2) additionally covers manual cd / git -C into a stale path, which is how this was actually hit.

Environment

  • Claude Code 2.1.214
  • Linux (WSL2)
  • git worktrees under the default .claude/worktrees/ location

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗