claude-code - 💡(How to fix) Fix [BUG] Workflow-tool agent worktrees are never cleaned up — neither WorktreeRemove hook nor default removal fires on teardown

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

When a Workflow-tool script spawns agents with opts.isolation: 'worktree', the worktrees are created correctly (via the configured WorktreeCreate hook), but on agent/workflow completion no teardown of any kind happens:

  1. The configured WorktreeRemove hook is never invoked (no process spawn, no log entry, no diagnostic).
  2. The documented default behavior — "the worktree is auto-removed if unchanged" — does not happen either.
  3. Both the worktree directories under .claude/worktrees/ AND their auto-created branches (named wf_<runId>-<n>) are left behind, even for agents that made zero changes (read-only probes).

Confirmed across a full CLI restart (so it is not a hook-config-loaded-at-startup staleness issue), and reproduced identically in three separate workflow runs (before the WorktreeRemove hook existed, after registering it mid-session, and in a fresh session after restart).

Root Cause

Related issues (same family, different entry points)

  • #37611 — WorktreeCreate hook configured ⇒ session cleanup flow skipped, WorktreeRemove never called (claude -w flow). This issue may share a root cause, but the Workflow-agent path additionally skips even the default unchanged-worktree removal.
  • #36205 — EnterWorktree tool ignores WorktreeCreate/WorktreeRemove hooks
  • #39281 — --worktree --tmux skips both hooks
  • #43730 — worktrees accumulate in .claude/worktrees/ with no lifecycle management
  • #34645 — the concurrent-creation .git/config.lock race (why our WorktreeCreate hook exists)

Fix Action

Workaround

Manual orchestrator cleanup after every worktree-isolated workflow run: git worktree listgit worktree remove --force <path> and git branch -d <name> per stray.

Code Example

export const meta = { name: 'probe', description: 'worktree teardown probe', phases: [{ title: 'Probe' }] }
phase('Probe')
const probes = await parallel([1, 2].map(i => () =>
  agent(`Read-only probe #${i}: report cwd and git rev-parse --short HEAD. Do not modify anything.`,
    { label: `probe-${i}`, model: 'haiku', schema: { type: 'object', properties: { cwd: { type: 'string' }, head: { type: 'string' } }, required: ['cwd', 'head'] }, isolation: 'worktree' })
))
return { probes }

---

$ git worktree list
G:/Code/<repo>                                      <sha> [master]
G:/Code/<repo>/.claude/worktrees/wf_373862c2-4e2-1  <sha> [wf_373862c2-4e2-1]
G:/Code/<repo>/.claude/worktrees/wf_373862c2-4e2-2  <sha> [wf_373862c2-4e2-2]

$ git branch --list 'wf_*'
+ wf_373862c2-4e2-1
+ wf_373862c2-4e2-2
RAW_BUFFERClick to expand / collapse

Environment

  • Claude Code: 2.1.165
  • Platform: Windows 11 Home (10.0.26200)
  • git: 2.45.1.windows.1
  • Repo: ordinary local git repository (not bare, single remote)
  • Hooks: user-level WorktreeCreate AND WorktreeRemove command hooks configured in ~/.claude/settings.json (both verified working when invoked directly — see "Hook sanity checks" below)

Summary

When a Workflow-tool script spawns agents with opts.isolation: 'worktree', the worktrees are created correctly (via the configured WorktreeCreate hook), but on agent/workflow completion no teardown of any kind happens:

  1. The configured WorktreeRemove hook is never invoked (no process spawn, no log entry, no diagnostic).
  2. The documented default behavior — "the worktree is auto-removed if unchanged" — does not happen either.
  3. Both the worktree directories under .claude/worktrees/ AND their auto-created branches (named wf_<runId>-<n>) are left behind, even for agents that made zero changes (read-only probes).

Confirmed across a full CLI restart (so it is not a hook-config-loaded-at-startup staleness issue), and reproduced identically in three separate workflow runs (before the WorktreeRemove hook existed, after registering it mid-session, and in a fresh session after restart).

Reproduction

  1. Configure a WorktreeCreate hook (ours serializes git worktree add under a lock per #34645) and a WorktreeRemove hook in ~/.claude/settings.json.
  2. From a session in a git repo, run a Workflow whose script spawns read-only agents with worktree isolation:
export const meta = { name: 'probe', description: 'worktree teardown probe', phases: [{ title: 'Probe' }] }
phase('Probe')
const probes = await parallel([1, 2].map(i => () =>
  agent(`Read-only probe #${i}: report cwd and git rev-parse --short HEAD. Do not modify anything.`,
    { label: `probe-${i}`, model: 'haiku', schema: { type: 'object', properties: { cwd: { type: 'string' }, head: { type: 'string' } }, required: ['cwd', 'head'] }, isolation: 'worktree' })
))
return { probes }
  1. Wait for the workflow to complete (agents return successfully; their reported cwds confirm they ran inside .claude/worktrees/wf_<runId>-1 / -2).
  2. Run git worktree list and git branch --list 'wf_*'.

Expected

Per the Workflow tool's own description ("isolation: 'worktree' gives the agent its own git worktree (auto-cleaned if unchanged)"): unchanged worktrees removed on teardown — via the configured WorktreeRemove hook since one is registered, or default removal otherwise.

Actual

$ git worktree list
G:/Code/<repo>                                      <sha> [master]
G:/Code/<repo>/.claude/worktrees/wf_373862c2-4e2-1  <sha> [wf_373862c2-4e2-1]
G:/Code/<repo>/.claude/worktrees/wf_373862c2-4e2-2  <sha> [wf_373862c2-4e2-2]

$ git branch --list 'wf_*'
+ wf_373862c2-4e2-1
+ wf_373862c2-4e2-2

Both worktrees and both branches persist indefinitely. The WorktreeRemove hook produced no output/log at any point (it logs on every invocation, including failures).

Hook sanity checks (the hooks themselves are fine)

  • WorktreeCreate hook: works — all isolated agents got correct worktrees branched from HEAD.
  • WorktreeRemove hook invoked manually with echo '{"cwd":"<repo>","name":"<wt-name>"}' | node <hook>.cjs: removes the worktree and its merged branch, exit 0. So the hook is functional; it is simply never called by the Workflow teardown path.

Why this matters more than disk clutter

#51596 reports that leftover agent branches are silently reused on name-prefix collision, contaminating future subagents with stale state. Combined with this bug, every worktree-isolated workflow run permanently accumulates wf_* branches that become a contamination vector for later runs, not just orphaned directories.

Related issues (same family, different entry points)

  • #37611 — WorktreeCreate hook configured ⇒ session cleanup flow skipped, WorktreeRemove never called (claude -w flow). This issue may share a root cause, but the Workflow-agent path additionally skips even the default unchanged-worktree removal.
  • #36205 — EnterWorktree tool ignores WorktreeCreate/WorktreeRemove hooks
  • #39281 — --worktree --tmux skips both hooks
  • #43730 — worktrees accumulate in .claude/worktrees/ with no lifecycle management
  • #34645 — the concurrent-creation .git/config.lock race (why our WorktreeCreate hook exists)

Workaround

Manual orchestrator cleanup after every worktree-isolated workflow run: git worktree listgit worktree remove --force <path> and git branch -d <name> per stray.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING