[BUG] Clean agent worktrees are never auto-cleaned when a worktree-isolated subagent finishes (2.1.278)

Status Open
Reported on v2.1.278
Maintainer reply None cached
Activity 2 comments · opened Sep 20, 2026

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 with E2BIG).
  • 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.

View original on GitHub ↗

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