Worktree cleanup deletes live worktrees when a repo is opened from both Windows and WSL (gitdir path-spelling mismatch makes every cross-side worktree look prunable)

Status Open
Maintainer reply None cached
Activity 0 comments · opened Aug 18, 2026

Summary

When one repository is reachable from both Windows (C:\dev\Projects\repo) and WSL (/mnt/c/dev/Projects/repo) — one working tree, one shared .git — each side's git reports the other side's live worktrees as prunable. Claude Code's automatic worktree cleanup then deletes those registrations, killing worktrees that are actively in use by a session on the other side.

This is a different root cause from #76144 feeding the same sink. There, gitdir is corrupted to the literal string .git; here gitdir is perfectly valid — just written in the other OS's path spelling.

Mechanism

.git/worktrees/<name>/gitdir stores an absolute path in the creating side's spelling, and neither side can stat the other's:

| registration created by | gitdir contains | exists on disk | Windows git | WSL git |
|---|---|---|---|---|
| Windows session | C:/dev/Projects/repo/... | yes | ok | prunable |
| WSL session | /mnt/c/dev/Projects/repo/... | yes | prunable | ok |

On Linux, C:/dev/... is a relative path (a component literally named C:); on Windows, /mnt/c/dev/... resolves to C:\mnt\c\dev\.... Neither exists, so each side concludes the other's worktrees are stale.

So prunable here does not mean stale — it means created by the other side. Anything that prunes on that signal destroys live state.

Reproduction

  1. From Windows, in a repo under C:\dev\Projects\repo, start a Claude Code session so it creates a worktree under .claude/worktrees/.
  2. From WSL: cd /mnt/c/dev/Projects/repo && git worktree list --porcelain → the Windows worktree is flagged prunable gitdir file points to non-existent location, while its directory plainly exists.
  3. From WSL: cd /mnt/c/dev/Projects/repo && claude — start a session, issue no git commands, exit.
  4. Back on Windows, .git/worktrees/<name> for the still-open session is gone.

Observed

A live session worktree vanished between two consecutive git calls, mid-task. Afterwards, in the still-running session:

  • every git command in that worktree fails with fatal: not a git repository: .../.git/worktrees/<name>
  • a git show > file redirect truncated a working file to zero bytes when git failed
  • git worktree repair cannot undo it — it repairs moved worktrees, not deleted registrations
  • the only recovery is hand-reconstructing the admin directory, or copying the changes into a fresh worktree to commit

In a repo with 40 worktree directories, only 18 registrations survived; the rest were casualties of prunes from one side or the other.

Suggested fix

An unstattable gitdir is not evidence of staleness. Cleanup should skip a registration whose gitdir path it cannot resolve, rather than treating unresolvable as dead — the conservative direction, since the cost of skipping a genuinely stale entry is a leftover directory, while the cost of a false positive is destroyed work in a running session.

Narrower options, if a general rule is unwelcome: recognise the C:/…/mnt/c/… correspondence before judging, or exempt registrations whose worktree directory exists under either spelling.

More broadly, and shared with #76144 and the other cleanup reports: prunable alone should not authorise automatic deletion. Git offers that flag to a human running an explicit command, who can see the repo state; automatic cleanup acting on the same flag has no such context.

Environment

  • Windows 11 Pro, WSL2 (Ubuntu), repo on NTFS accessed from WSL via /mnt/c with metadata automount
  • Both a native-Windows and a WSL Claude Code installation, same repository
  • Trigger is a WSL-side session start with no explicit git commands, so it cannot be caught by allowlists or hooks

Related

  • #76144 — same symptom (healthy worktree flagged prunable, then reclaimed), different cause (gitdir written as literal .git). A fix for that truncation would not address this.
  • #50909 — same Windows/WSL shared-repo setup destroying work, via a different mechanism (git reset --hard collision between concurrent sessions).

View original on GitHub ↗