claude --worktree <existing-name> does not register worktree as session-managed; /exit → remove reports false success
Summary
Two related bugs in the worktree lifecycle:
claude --worktree <existing-name>(andclaude --worktree <name> --resume <id>) does NOT register the worktree as session-managed. The session JSON at~/.claude/sessions/<pid>.jsonis missing theworktreefield thatExitWorktree's scope check requires./exit → removeprints "Worktree removed. Uncommitted changes were discarded." even when the worktree is NOT actually removed. The success message is reported without verifyinggit worktree removeran or succeeded.
Environment
- Claude Code version: 2.1.139
- Platform: macOS (Darwin 25.4.0)
- Shell: zsh
Reproduction
- Create a worktree via the documented hook path:
````
cd <main-repo>
claude --worktree my-feature
# WorktreeCreate hook fires, worktree created at .claude/worktrees/my-feature
- Exit the session (
/exit → keep) or just close the terminal. - Re-launch into the existing worktree:
````
cd <main-repo>
claude --worktree my-feature --resume <prior-session-id>
- In the new session, run
/exitand select remove.
Expected
- Per docs, interactive
--worktreesessions should support cleanup via the exit prompt. - "Worktree removed" should mean the worktree IS removed (
git worktree listno longer shows it, directory deleted).
Actual
- Message printed: "Worktree removed. Uncommitted changes were discarded."
git worktree liststill shows the worktree.- The directory still exists on disk.
- Session JSON shows no
worktreefield:
``jsonworktree
{
"pid": 45938,
"sessionId": "2355cf12-c52f-4422-9b40-98eb9552b990",
"cwd": "/Users/.../worktrees/up-skill",
"startedAt": 1778600722769,
"version": "2.1.139",
"entrypoint": "cli",
"kind": "interactive"
// NO field``
}
(Compare to a session opened via EnterWorktree in-session, which presumably does have the worktree field — though I can't verify that path because EnterWorktree's scope correctly excludes pre-existing worktrees.)
Why this matters
Teams using claude --worktree as the canonical entry point for isolated dev environments (per-branch Supabase + Doppler bindings, etc.) rely on /exit → remove to trigger cleanup. Today the success message gives false confidence that cleanup happened — operators don't realize they need to manually git worktree remove until they find stale entries piling up in git worktree list.
In my own checkout I have 8 prunable worktree entries that accumulated this way, plus the active worktree that just "remove"d but is still there.
Related
- #36205 —
EnterWorktreeignoresWorktreeCreate/WorktreeRemovehooks (open, unfixed) - #31969 — Enter/resume existing worktrees
Suggested fixes
- When
--worktree <name>matches an existing worktree, register it on the session object (same in-memory state thatEnterWorktreepopulates) soExitWorktree's scope check accepts it. - The
/exit → removeUI should verifygit worktree removesucceeded before printing "Worktree removed." Print the actual error otherwise. - (Bonus) Document or add a hook output / SessionStart field that lets project hooks register a worktree as session-managed — useful for teams that bootstrap worktrees through their own scripts.
Workaround
None on the user side. The session-state plumbing is in-memory and not influenceable via documented hook surface area (I verified this empirically — injecting worktree: {...} into ~/.claude/sessions/<pid>.json via a SessionStart hook had no effect).
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗