[BUG] Worktree recycling destroys live sibling sessions' worktrees — including locked ones and uncommitted work
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?
Summary
Under heavy multi-session use of one repository (many concurrent Claude Code sessions, peak ~18 registered worktrees), Claude Code's worktree recycling repeatedly deletes worktrees that belong to other live sessions. It does not respect git worktree lock, and recycling a worktree that contains uncommitted/untracked files destroys them (recycle = delete + recreate fresh checkout). One session's worktree was destroyed five times in a single session; across sessions the same day we permanently lost uncommitted work once (7 dirty files) and nearly lost a 23-file feature implementation.
Observed timeline (one session, 2026-07-13)
- Session starts in registered worktree
.claude/worktrees/<name>(clean, tip == main — it was a read-only analysis session). - Worktree deleted out from under the session mid-command: "Working directory ... was deleted; shell cwd recovered to ...". No harness notice at this point.
- Session recreates the worktree manually (
git worktree add) and locks it:git worktree lock --reason "ACTIVE Claude session — do not prune" <path>. Deleted again ~3 minutes later. Metadata under.git/worktrees/was cleanly removed — the lock was not honored (plaingit worktree remove, even with a single--force, refuses locked trees; whatever removed it went past that). - Session creates a worktree outside the repo at
/private/tmp/<name>(same branch) and edits files there. Deleted minutes later with uncommitted changes in it. - Harness then emits, twice in succession: "The git worktree at ... was recycled. This session now operates on a fresh worktree at ..."
- Finally: "The git worktree at ... was deleted. This session now operates on the origin repository at ..." — i.e. after losing the race repeatedly, the session is dropped into the shared main checkout, the highest-contention place possible, where its writes collide with every other session.
Corroborating losses (same repo, same day, other sessions)
- A session's worktree holding 7 dirty uncommitted files (feature WIP) was swept mid-session; the files were permanently lost.
- A worktree holding a 23-file uncommitted feature implementation was deleted; the building session's own commit message recorded the work as "lost to repeated worktree recycling before commit". (Recovered only because that session had copied the files into the main checkout minutes before.)
- A third session reported: "my worktree got deleted mid-command by a parallel session (the 18-worktree contention)". Every session perceives a hostile "parallel session"; the recycle notices in step 5 indicate the deletions come from Claude Code's own worktree lifecycle.
Why this seems like a bug
Docs describe worktrees as "auto-cleaned if unchanged" — but a live session's in-use worktree qualifies merely because its tree is clean and its branch tip equals main, which is also the signature of a freshly started or read-only session. And dirty worktrees were deleted too.
Suggested fixes
- Ownership/liveness check before recycle: only remove worktrees created by the current session, or lock worktrees at creation with
--reason "pid <N> session <id>"and probe liveness before removal (this also makes liveness visible to external tooling). - Honor
git worktree lockunconditionally — never double-force a locked tree. - Never delete dirty: if
git status --porcelainis non-empty, refuse or move the tree aside (e.g. rename to<name>.orphaned-<ts>) instead of deleting. - Don't fall back to the origin repository after repeated recycles — that converts an isolation mechanism into a shared-checkout collision.
- Log which process/heuristic triggered each recycle in the notice, so multi-session users can diagnose.
What Should Happen?
- A worktree registered to a live session is never deleted by another session's harness.
git worktree lock(git's native "in use" claim) is honored.- A worktree containing uncommitted or untracked files is never deleted silently; recycling preserves (or refuses on) dirty state.
- A session is never silently relocated into the shared main checkout while sibling sessions are active.
Error Messages/Logs
Bash tool, mid-session (no prior notice):
Working directory "/Users/<user>/dev/<repo>/.claude/worktrees/<name>" was deleted; shell cwd recovered to "/Users/<user>". Re-issue your command (it will run from the recovered directory).
Harness system reminders, later in the same session (back to back):
The git worktree at /Users/<user>/dev/<repo>/.claude/worktrees/<name-1> was recycled. This session now operates on a fresh worktree at /Users/<user>/dev/<repo>/.claude/worktrees/<name-2>, checked out to the same branch (<branch>). Absolute paths from earlier in the conversation that reference the old worktree no longer exist.
The git worktree at /Users/<user>/dev/<repo>/.claude/worktrees/<name-2> was recycled. This session now operates on a fresh worktree at /Users/<user>/dev/<repo>/.claude/worktrees/<name-3> ...
The git worktree at /Users/<user>/dev/<repo>/.claude/worktrees/<name-3> was deleted. This session now operates on the origin repository at /Users/<user>/dev/<repo>. File paths from earlier in the conversation that reference the worktree no longer exist.
Environment: Claude Code 2.1.207 (desktop app), macOS 26.6, git 2.50.1 (Apple Git-155). Many concurrent sessions against one repo; peak ~18 registered worktrees.
Steps to Reproduce
- Pick one repository. Start 6–10 concurrent Claude Code sessions against it, each isolated in a
.claude/worktrees/<name>worktree (the default worktree isolation). - Keep one session read-only (analysis/review work): its tree stays clean and its branch tip stays == main.
- Watch
git worktree listwhile other sessions start and stop.
Observed results:
- The read-only session's worktree is removed by sibling activity within minutes ("Working directory ... was deleted" on its next Bash call, later "The git worktree at ... was recycled" notices).
- Repeat with
git worktree lock --reason "in use" <path>applied — the worktree is still removed (metadata cleanly deleted, so it is a real remove/prune, not an rm). - Scatter untracked files into a candidate worktree first — they are destroyed on recycle.
- After several recycle rounds the session is relocated into the origin repository (shared main checkout).
Related issues (this report is distinct from both):
- #74386 covers a user-directed discard with no liveness signal; this report shows the AUTOMATIC recycler doing the same unprompted, and bypassing
git worktree lock— a liveness signal that does exist. - #74782 (closed) covers the mid-session teardown + silent fall-through to the parent repo's .git; still reproducible on 2.1.207, and the "operates on the origin repository" fallback here recreates exactly the shared-checkout hazard that issue describes.
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.207 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Other
Additional Information
Sessions run in the Claude Code desktop app (macOS) — hence Terminal/Shell = Other. Cursor IDE worktrees under ~/.cursor/worktrees/<repo>/ also existed in the same repo but were NOT the actor: no repo script or IDE process was running at the deletion times, and the harness's own "was recycled" notices account for the removals.
User-side mitigations we've adopted meanwhile, which may be useful signal for the fix design: worktree-locking at creation with a PID reason, an age-floor + lock-respect patch to our repo's own cleanup script, and falling back to plain git clone (own .git, invisible to git worktree list) as the only isolation that survives — which defeats the purpose of the built-in worktree isolation.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗