Windows: session creation reuses stale non-empty .claude/worktrees dir, skips worktree registration, and checks the session branch out in the PARENT repo (HEAD flip under concurrent sessions)
Open 💬 3 comments Opened Jul 11, 2026 by overseer-marketing
Environment
- Windows 11 Pro (10.0.26200), NTFS
- Claude Code desktop app (sessions created via the app's worktree affordance);
claude --versionreports 2.1.198 - One repo, many concurrent desktop sessions, each assigned a dir under
.claude/worktrees/<slug>/
Summary
When the desktop app creates (or resumes) a session whose designated worktree directory already exists and is non-empty, worktree registration is silently skipped — git worktree add refuses a non-empty dir at every force level — and the harness instead checks the session's branch out in the parent repository. Two consequences:
- The parent checkout's HEAD flips onto the new session's branch as a side-effect of session creation/resume, under any concurrent session or automation using that checkout.
- The new session runs with cwd = a plain, unregistered dir. Every git command resolves upward to the parent repo, while the session banner still claims "You are operating in a git worktree." The session is effectively un-isolated (a "ghost session").
Why stale dirs exist (the loop)
- On Windows,
git worktree removefor a closed session often exits 255: the app's file-watcher holds a lock, so git deregisters the worktree but cannot delete the folder. The leftover retains.claude/settings.local.json(the settings copy the app writes into each worktree). - The app later reuses such a leftover dir as the worktree target for a brand-new or resumed session (observed: one dir hosted five successive sessions over four days). The first session in a fresh dir gets a healthy registered worktree; every session reusing a leftover becomes a ghost.
Evidence (2026-07-09/10, one workspace)
- Five parent-repo reflog entries
checkout: moving from X to <new-session-branch>correlate 1:1, within seconds, with ghost session creations/resumes. - Every ghost session's cwd dir pre-existed its session by 1-4 days; every healthy session's dir was created fresh at registration time.
- In the session list, every ghost session shows cwd-slug != branch-slug; healthy sessions match.
- Concrete damage: at 15:56:03 a new session's creation flipped the parent's HEAD mid-operation of a live ghost session — the ghost's cherry-pick, aimed at its own branch, landed on the seconds-old other session's branch instead, stranding a commit (recovered from the reflog).
- Healing observation: two sessions whose leftover dirs had been fully deleted re-registered healthy worktrees on resume; two others resumed into surviving leftovers, ghosted, and flipped the parent's HEAD.
Reproduction
mkdir -p <repo>/.claude/worktrees/<slug>/.claude && echo '{}' > <repo>/.claude/worktrees/<slug>/.claude/settings.local.json- Start/resume a desktop session that resolves to
<slug>as its worktree dir. - Observe: the slug is absent from
git worktree list; the parent reflog gains acheckout: moving from <old> to <session-branch>entry stamped at session start; from inside the session,git rev-parse --show-toplevelreturns the parent root andgit statusshows../../..-prefixed paths.
Expected behavior (any of)
- Refuse to start the session (or fall back to a fresh slug) when the target dir exists but is not a registered worktree;
- Clear/recreate the dir, then run
git worktree addnormally; - Never fall back to checking the session's branch out in the parent repo — that mutates shared state that concurrent sessions and scheduled automation depend on.
Related but distinct
- #45024 added deliberate reuse of an existing worktree for a branch — this report concerns reuse of an existing unregistered dir.
- #61630 covers a worktree disappearing mid-run; here the worktree never exists in the first place.
- #60624 was
EnterWorktree-specific; this is the desktop session-creation path (EnterWorktreeis never called).
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗