claude --worktree <existing-name> does not register worktree as session-managed; /exit → remove reports false success

Resolved 💬 4 comments Opened May 12, 2026 by bvallelunga Closed May 16, 2026

Summary

Two related bugs in the worktree lifecycle:

  1. claude --worktree <existing-name> (and claude --worktree <name> --resume <id>) does NOT register the worktree as session-managed. The session JSON at ~/.claude/sessions/<pid>.json is missing the worktree field that ExitWorktree's scope check requires.
  2. /exit → remove prints "Worktree removed. Uncommitted changes were discarded." even when the worktree is NOT actually removed. The success message is reported without verifying git worktree remove ran or succeeded.

Environment

  • Claude Code version: 2.1.139
  • Platform: macOS (Darwin 25.4.0)
  • Shell: zsh

Reproduction

  1. 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
``

  1. Exit the session (/exit → keep) or just close the terminal.
  2. Re-launch into the existing worktree:

``
cd <main-repo>
claude --worktree my-feature --resume <prior-session-id>
``

  1. In the new session, run /exit and select remove.

Expected

  • Per docs, interactive --worktree sessions should support cleanup via the exit prompt.
  • "Worktree removed" should mean the worktree IS removed (git worktree list no longer shows it, directory deleted).

Actual

  • Message printed: "Worktree removed. Uncommitted changes were discarded."
  • git worktree list still shows the worktree.
  • The directory still exists on disk.
  • Session JSON shows no worktree field:

``json
{
"pid": 45938,
"sessionId": "2355cf12-c52f-4422-9b40-98eb9552b990",
"cwd": "/Users/.../worktrees/up-skill",
"startedAt": 1778600722769,
"version": "2.1.139",
"entrypoint": "cli",
"kind": "interactive"
// NO
worktree 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 — EnterWorktree ignores WorktreeCreate/WorktreeRemove hooks (open, unfixed)
  • #31969 — Enter/resume existing worktrees

Suggested fixes

  1. When --worktree <name> matches an existing worktree, register it on the session object (same in-memory state that EnterWorktree populates) so ExitWorktree's scope check accepts it.
  2. The /exit → remove UI should verify git worktree remove succeeded before printing "Worktree removed." Print the actual error otherwise.
  3. (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).

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗