Background sessions dispatched from a non-git directory can never be deleted from the agents view: "worktree could not be removed (no git root for the worktree)"

Status Fixed / completed
Reported on v2.1.211
Maintainer reply ✓ Yes — bcherny
Activity 4 comments · opened Jul 16, 2026 · closed Aug 17, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

Environment

  • Claude Code 2.1.211 (native build, macOS arm64, Darwin 24.6); affected jobs were created on 2.1.210 and 2.1.211
  • Background sessions dispatched from the agents view (claude agents), default worktree isolation

Summary

If a background session is dispatched from a directory that is not a git repository but contains git repositories (a multi-repo workspace folder like ~/Code), and the session then enters a worktree of one of the nested repos (which background-session policy requires before editing, and which EnterWorktree explicitly supports for nested repos on first entry), the session can never be deleted from the agents view afterwards. Every delete attempt fails with:

not deleted · worktree could not be removed (no git root for the worktree)

The state is stable, so these undeletable rows accumulate — one per background job dispatched this way.

Steps to reproduce

  1. cd ~/Code — a plain folder that is not a git repo, containing e.g. ~/Code/repo-a (a git repo).
  2. Open claude agents and dispatch a background session, e.g. "look into issue #123" (which the agent resolves against repo-a).
  3. The agent calls EnterWorktree before editing (as the background-session system prompt requires). Since the launch cwd is not a repo, the worktree is created for the nested repo: ~/Code/repo-a/.claude/worktrees/<name>. The job's state.json records worktreePath and worktreeBranch, while originCwd remains ~/Code.
  4. Let the job run to completion (state done, PR pushed, worktree clean).
  5. Press delete (x) on the session in the agents view.

Expected: worktree removed (it is clean, fully pushed, and its lock is held by a dead pid), session deleted.

Actual: not deleted · worktree could not be removed (no git root for the worktree), forever.

Root cause (from the 2.1.211 bundle)

In the job-deletion path (deleteJob), the git root used for worktree removal is resolved as:

let p = tp(r.originCwd ?? a) ?? void 0   // findGitRoot(state.originCwd ?? state.worktreePath)

The fallback to the worktree path only applies when originCwd is unset. When originCwd is set but does not resolve to a git repository (the multi-repo workspace case), p is undefined, and the removal helper hard-fails before doing anything:

async function removeAgentWorktree(e, t, r, n, o) {
  ...
  if (!r) return log("Cannot remove agent worktree: no git root provided"),
    { outcome: "failed", errorSummary: "no git root for the worktree" };
  ...
}

which surfaces as keptReason: "remove_failed"not deleted · worktree could not be removed (no git root for the worktree).

Notes:

  • All the safety checks that could otherwise keep the worktree (dirty, unpushed, in-use, live lock) pass or are bypassed; the failure is purely the root resolution.
  • The same permanent failure occurs when the worktree directory has already been deleted externally (the git status probe errors, and the flow still reaches removeAgentWorktree with no git root).
  • The worktree creation path already computes and returns the git root ({worktreePath, worktreeBranch, headCommit, gitRoot}), but gitRoot is not persisted into the job's state.json, so deletion has to re-derive it — from the wrong directory.

Suggested fix

Any of:

  1. Fall back through candidates: findGitRoot(originCwd) ?? findGitRoot(worktreePath) ?? deriveMainRepoFromGitdirPointer(worktreePath) (the .git file inside a linked worktree points at <main-repo>/.git/worktrees/<name>, which identifies the owning repo even when originCwd is useless).
  2. Persist gitRoot in the job's state.json at worktree creation time (it is already computed there) and use it at deletion time. This also covers the case where the worktree directory was deleted externally before the session was deleted.

Workaround

Edit ~/.claude/jobs/<jobid>/state.json and set originCwd to the root of the repository that owns the worktree (the path prefix of worktreePath before /.claude/worktrees/). The next delete from the agents view then proceeds through the normal cleanup path, including all its safety checks, and succeeds.

View original on GitHub ↗

3 Comments

bcherny collaborator · 1 month ago

Fix incoming

mattfelten · 1 month ago

This is biting me also on 2.1.212

andreasgerner · 23 days ago

Fixed for me after updating 2.1.220.

Showing cached comments. Read the full discussion on GitHub ↗