[BUG] Clean agent worktrees are never auto-cleaned when a worktree-isolated subagent finishes (2.1.278)
What happens
The Agent tool documents isolation: "worktree" as "gives the agent its own git worktree (auto-cleaned if unchanged)". In practice the worktree is never removed when the subagent finishes, even when it is provably unchanged. They accumulate in the parent repo's .claude/worktrees/ and stay registered in git worktree list.
In one long session I accumulated six of them (four read-only reviewers, one cross-repo agent that did its writing in a different repo, one agent that died on an API rate limit). All six were clean; I removed them by hand with git worktree remove --force.
Controlled reproduction (v2.1.278)
Dispatched one general-purpose subagent with isolation: "worktree", model haiku, whose entire prompt was five read-only commands (pwd, two ls, two git status) and an instruction to create/edit/delete nothing. It completed normally.
What the agent itself reported from inside its worktree:
$ pwd
/Users/<user>/<projects>/<repo>/.claude/worktrees/agent-<id>
$ ls -la .claude/ | head -20
drwxr-xr-x@ 9 ... 288 Sep 20 10:20 .
drwxr-xr-x@ 31 ... 992 Sep 20 10:20 ..
lrwxr-xr-x@ 1 ... 19 Sep 20 10:20 agents -> ../agents/reviewers
lrwxr-xr-x@ 1 ... 18 Sep 20 10:20 commands -> ../agents/commands
-rw-r--r--@ 1 ... 1134 Sep 20 10:20 hookify.warn-hextra-weight-zero.local.md
-rw-r--r--@ 1 ... 210 Sep 20 10:20 launch.json
lrwxr-xr-x@ 1 ... 15 Sep 20 10:20 rules -> ../agents/rules
-rw-r--r--@ 1 ... 1641 Sep 20 10:20 settings.json
lrwxr-xr-x@ 1 ... 16 Sep 20 10:20 skills -> ../agents/skills
$ ls -l .claude/settings.local.json
ls: .claude/settings.local.json: No such file or directory
$ git status --porcelain
(empty)
$ git status --porcelain --ignored=matching -- .claude/
(empty)
Checked from the parent session ~1 minute after the agent finished:
$ ls -1 .claude/worktrees/
agent-<id>
$ git worktree list | grep agent-
/Users/<user>/<projects>/<repo>/.claude/worktrees/agent-<id> 725158fc (detached HEAD)
$ git -C .claude/worktrees/agent-<id> status --porcelain
(empty)
The worktree is still on disk, still registered, and still clean.
Why this is not #84027
#84027 reports the same outcome via a different mechanism: the harness writes .claude/settings.local.json into the worktree, dirtying it and disqualifying it from cleanup. That is not what happens here — there is no settings.local.json in the worktree at all, and git status --porcelain --ignored=matching -- .claude/ is empty, so nothing is being hidden by an ignore rule either. A clean worktree is simply not being reaped. If both reports share a root cause (the reaper never running), #84027's dirtiness explanation may be incidental rather than causal.
Possibly related: #74708 (WorktreeRemove hook never fires when a worktree is removed at session exit), #76377 (background/bridge sessions leak .claude/worktrees), #92342 (feature request: a hook after a session releases its worktree).
Why it matters
- Silent, unbounded growth of full checkouts in the parent repo. Each is a complete working tree.
- Every leaked worktree stays in
git worktree list, which on macOS feeds #82840 (the seatbelt profile grows one deny entry per registered worktree until sandboxed spawns die withE2BIG). - Read-only agents are the worst case: dispatching a reviewer that writes nothing still leaks a checkout, and the org convention many of us follow (
isolation: "worktree"for any agent type whose grant includes Edit/Write) means read-only reviewers get one by default.
Ask
Please investigate why the "auto-cleaned if unchanged" path does not fire on normal subagent completion, and fix it — or, if cleanup is intentionally deferred, document when it is expected to happen and provide a supported way to trigger it.
Environment
- Claude Code 2.1.278
- macOS 26.6.2
- git 2.50.1 (Apple Git-155)
- Parent session working in a separate git worktree of the same repo (an fr-isolation workspace outside the repo tree); agents dispatched from there.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗