Worker fork / subagent is not woken by the completion of its LAST pending background task
Summary
A worker fork (/fork) — and, as far as I can tell, any subagent — that launches background Bash commands with run_in_background: true is re-invoked ("woken") by each background task's completion notification except the last pending one. The completion of whichever task finishes when no other background work is still pending is never delivered, so the fork is never given a turn to observe it.
A practical corollary: a fork that launches a single background task and ends its turn is never woken, because that one task is also the last pending task.
Minimal repro
- From an interactive session, start a worker fork with this directive:
> Launch two background bash commands, then end your turn without polling:
> (a) sleep 10 && echo done_A with run_in_background: true
> (b) sleep 30 && echo done_B with run_in_background: true
> On each turn, report which completion notification (if any) just arrived.
- The fork launches both with
run_in_background: trueand ends its turn. Do not message the fork after this point. - Wait > 30s.
Expected
The fork is re-invoked twice:
- at ~10s, by command (a)'s completion
- at ~30s, by command (b)'s completion
Actual
The fork is re-invoked once, at ~10s, by command (a)'s completion — correct, and (b) is still pending at that moment. When command (b) (the last pending task) completes at ~30s, the fork is not re-invoked; (b)'s completion notification is never delivered. The fork's transcript ends after the (a)-wake turn, even though (b) finished successfully (verifiable: its output file contains done_B).
Actual run that surfaced this (3 background tasks)
The fork was given this directive: launch a 120s background command, launch a 60s background command, and when the 60s returns, launch an 80s background command; report which commands launched and which surfaced a completion notification. The parent then deliberately sent the fork no messages at all after the initial directive, so every subsequent turn was necessarily completion-driven.
Timeline (task IDs from the run; T0 = first launch):
| Time | Event | Pending background tasks at this moment | Fork woken? |
|------|-------|------------------------------------------|-------------|
| T0+0s | Fork launches 120s (blvrvtzts) and 60s (b19z0yxe3), ends turn | {120s, 60s} | — |
| T0+~60s | 60s (b19z0yxe3) completes | {120s} (≥1 other pending) | Yes — fork woken, launches 80s (b9vgy331p), ends turn |
| T0+~120s | 120s (blvrvtzts) completes | {80s} (≥1 other pending) | Yes — fork woken, reports tally, ends turn |
| T0+~150s | 80s (b9vgy331p) completes | {} (nothing else pending) | No — fork never re-invoked |
The 80s task did finish cleanly (its output file contained done_80s), but the fork's transcript ends at the 120s-wake turn — there is no turn for the 80s completion. The fork had signed off its previous turn predicting "the 80s should be the last wake"; that wake never arrived.
Two points this run establishes beyond the minimal repro:
- The wakes are genuinely spontaneous, not an artifact of the parent prompting the fork. The parent sent zero messages after the directive, and the fork's own transcript reasoning on the 60s-wake turn reads: "A completion notification from the 60s task woke up the idle fork without any explicit message." So a background completion does re-invoke an idle fork on its own — the bug is specifically that the last one does not.
- It is about completion order, not launch order. The 80s was launched mid-run (on the 60s-wake turn) and still behaved identically: it was the last task pending when it completed, and its notification was dropped.
Observed pattern
A completion notification re-invokes the fork iff at least one other background task is still pending at the moment it completes. The completion of the last outstanding task is dropped — there is no subsequent turn for it to be delivered on.
This suggests the fork's wake subscription is torn down once it has no remaining in-flight background work, so the final completion arrives with nothing left to receive it.
Impact
Any fork/subagent that backgrounds work and relies on completion notifications will silently miss the last one. In the common single-task case it misses the only one, so the fork appears to "never get notified" and must fall back to polling the output file — which defeats the purpose of backgrounding.
Environment
- Claude Code CLI, version 2.1.176
- Worker fork (
/fork); model claude-opus-4-8 (also reproduced under claude-haiku-4-5) - Linux
Related
- #60001 — completion notifications drop for trailing agents in ≥3-way parallel
run_in_backgrounddispatches. Related (dropped completions) but distinct: that is count/GC-threshold driven across parallel agents; this is the deterministic loss of the last pending task's notification within a single fork, independent of parallelism count. - #68062 — docs request on whether in-place forking tears down the parent's in-flight background tasks. Adjacent (fork × background-task lifecycle) but about the parent's tasks at fork time, not a fork being woken by its own tasks.
---
Reproduced and characterized by Claude (Opus 4.8) running in Claude Code, after a user noticed the behavior. Co-Authored-By: Claude <noreply@anthropic.com>
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗