[BUG] ExitWorktree (remove) reports success but orphans the worktree (dir + admin entry + branch); intermittently corrupts parent repo core.bare
Environment
- Claude Code 2.1.177 (CLI)
- macOS 26.5.1 (Darwin 25.5.0), git 2.50.1 (Apple Git-155)
worktree.baseRef: "fresh". AWorktreeCreatehook relocates worktrees out of.claude/worktrees/to~/code/.worktrees/<repo>/<name>/, so the session cwd becomes that path. No branch renames involved.
Summary
Two related failures in ExitWorktree action="remove" when the session's working directory is inside the worktree being removed:
- (100% reproducible) Orphaned worktree despite a success message. The first
removeis refused (Could not verify worktree state … Refusing to remove without explicit confirmation); after re-invoking withdiscard_changes: trueit returnsExited and removed worktree at …, but the worktree directory, its.git/worktrees/<name>/admin entry, and theworktree-<name>branch all remain.git worktree prunewon't reap it (the dir is still present). This differs from #67196's hypothesis that the leftover branch is caused by a mid-sessiongit branch -mrename — here no rename is involved; a vanilla create → work → commit → push → exit cycle leaks.
- (Intermittent, severe) Parent-repo
core.barecorruption. In one occurrence the same teardown leftcore.bare = truein the primary repo's shared.git/config. That breaks the whole repo:git status/git worktree/ commit all fail withfatal: this operation must be run in a work tree, and git's "refusing to fetch into a checked-out branch" safety rail is disabled — so a latergit fetch origin main:mainadvanced the ref while leaving the working tree frozen, surfacing in the next session as a phantom "staged revert" of everything that had changed since. It did not recur on clean single cycles; it appears tied to the messy refuse → retry path from symptom 1.
Steps to reproduce (the reliably-reproducible orphan)
EnterWorktree(name mode) → session cwd becomes the worktree root.- Without
cd-ing out, callExitWorktree { action: "remove" }→ refused:Could not verify worktree state … Refusing to remove without explicit confirmation. ExitWorktree { action: "remove", discard_changes: true }→ returnsExited and removed worktree at ….- From the primary checkout, observe the leak:
git worktree liststill lists the worktree- the worktree directory still exists on disk
git branch --list 'worktree-<name>'is still presentgit worktree pruneis a no-op (dir present)
Cleanup requires a manual git worktree remove --force <path> + git branch -D worktree-<name>.
Observed 3× in one session — every time the session cwd was inside the worktree at removal.
What I verified about the core.bare variant
Plain git worktree remove --force <wt> and rm -rf <wt> && git worktree prune run from inside a worktree do not flip core.bare, so the mutation lives in ExitWorktree's own removal path, not in git. The WorktreeCreate hook does a plain git worktree add and never touches core.bare, and there is no custom remove hook.
Expected behavior
ExitWorktree action="remove" should either fully remove the worktree (directory + .git/worktrees/<name> admin entry + branch) or report failure — never (a) claim success while leaving the worktree registered, or (b) mutate the parent repo's core.bare (or any other shared .git/config key).
Workaround
cdto the primary checkout before callingExitWorktree.- After teardown, verify with
git worktree listandgit config core.bare(must befalse), and clean up any orphan manually.
Related issues
- #67196 — same family (EnterWorktree writes
core.hooksPathinto the shared repo config; ExitWorktree doesn't restore it, and notes a branch left behind after a rename). This report adds two things: the orphan happens without any rename, and the shared-config mutation can also hitcore.bare, not justcore.hooksPath. - #48250 — ExitWorktree no-op for Desktop-created worktrees (different cause).
- #62724 — feature request for merge verification on ExitWorktree (related to the "could not verify worktree state" refusal in step 2).
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗