[BUG] ExitWorktree (remove) reports success but orphans the worktree (dir + admin entry + branch); intermittently corrupts parent repo core.bare

Open 💬 2 comments Opened Jun 20, 2026 by jsolly

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". A WorktreeCreate hook 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:

  1. (100% reproducible) Orphaned worktree despite a success message. The first remove is refused (Could not verify worktree state … Refusing to remove without explicit confirmation); after re-invoking with discard_changes: true it returns Exited and removed worktree at …, but the worktree directory, its .git/worktrees/<name>/ admin entry, and the worktree-<name> branch all remain. git worktree prune won't reap it (the dir is still present). This differs from #67196's hypothesis that the leftover branch is caused by a mid-session git branch -m rename — here no rename is involved; a vanilla create → work → commit → push → exit cycle leaks.
  1. (Intermittent, severe) Parent-repo core.bare corruption. In one occurrence the same teardown left core.bare = true in the primary repo's shared .git/config. That breaks the whole repo: git status / git worktree / commit all fail with fatal: 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 later git fetch origin main:main advanced 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)

  1. EnterWorktree (name mode) → session cwd becomes the worktree root.
  2. Without cd-ing out, call ExitWorktree { action: "remove" } → refused: Could not verify worktree state … Refusing to remove without explicit confirmation.
  3. ExitWorktree { action: "remove", discard_changes: true } → returns Exited and removed worktree at ….
  4. From the primary checkout, observe the leak:
  • git worktree list still lists the worktree
  • the worktree directory still exists on disk
  • git branch --list 'worktree-<name>' is still present
  • git worktree prune is 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

  • cd to the primary checkout before calling ExitWorktree.
  • After teardown, verify with git worktree list and git config core.bare (must be false), and clean up any orphan manually.

Related issues

  • #67196 — same family (EnterWorktree writes core.hooksPath into 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 hit core.bare, not just core.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).

View original on GitHub ↗

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