[BUG] Nested background subagent never re-woken by its own children — completion notifications route to the top-level session (2.1.224)
Description
A background subagent (a /code-review skill running as a forked background agent) spawned its own background children (8 "finder" agents plus run_in_background Bash commands). When those children finished, their completion notifications were delivered to the top-level session instead of re-waking the parent subagent that spawned them. The parent also was not re-woken when its own background Bash command (sleep 120 used as a wait timer) completed. Result: the parent stalls indefinitely mid-orchestration ("Waiting for the last three finder agents") until the top-level session manually resumes it via SendMessage. After resuming, it stalls again the same way on the next wait, so a multi-stage orchestration needs a manual nudge per stage.
Possibly the same root cause as #68065 (parent-child association / notification routing for nested background agents), but the symptom differs: here the children's notifications carry their own correct task IDs — they are just delivered one level too high (to the main session), while the level that is blocked on them (the parent subagent) never gets woken.
Environment
- Claude Code 2.1.224 (current stable at time of filing), CLI entrypoint
- Linux WSL2 (6.6.87.2-microsoft-standard-WSL2), bash
- Model: claude-fable-5 (main session and subagents)
Sequence observed (from session transcripts, single occurrence, timestamps local)
- Main session invokes the
code-reviewskill → runs as forked background subagent (taska9961e3668c6acc7e). - Reviewer spawns ~8 background finder subagents; five finish by ~19:39.
- 19:42:28 — reviewer launches
Bash {command: "sleep 120", run_in_background: true}to wait for the last three finders, then stops with text "Waiting for the last three finder agents." - The reviewer's stop fires a
<task-notification>withstatus: completedto the main session — even though it is logically mid-task with pending children. - The last finders finish by 19:41–19:44 (transcripts complete). No notification re-wakes the reviewer. The
sleep 120completion (~19:44:29) also does not re-wake it. - The finders' completion notifications arrive at the main session (correct task IDs, wrong recipient level — the main session did not spawn them).
- Main session sends the reviewer a
SendMessage→ reviewer resumes, re-resumes finders/verifiers, and stalls again the same way ("I'll consolidate once their notifications arrive"), with the next round of child notifications again delivered to the main session. - Main session ultimately collected the children's results from their transcript files and finished consolidation itself.
Expected
A background subagent that spawns background children (agents or Bash commands) should be re-invoked when those children complete, the way the main session is. Its "completed" notification to its own parent should not fire while it is deliberately waiting on tracked background children.
Impact
Any skill or subagent that orchestrates its own background fan-out stalls at every join point unless a human (or the top-level session) notices and manually resumes it. Silent — the top-level session just sees a premature "completed" notification with an in-progress-sounding result text.
Notes
- The task-notification's own note text says "A task-notification fires each time this agent stops with no live background children of its own" — but the reviewer stopped while it did have a live background child (the sleep), and the children finishing later never woke it, so the documented model doesn't match observed behavior at nesting depth ≥ 2.
- Happy to provide redacted transcript excerpts if useful.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Ran into the same routing gap while building an orchestration harness where background subagents spawn their own children.
The core problem seems to be that completion notifications carry the spawning session's ID statically at spawn time, so when a second-level subagent finishes, the message routes to whoever the first-level subagent knew as "parent" at creation -- which is the top-level session, not the intermediate subagent that's currently waiting.
A few things that helped us partially work around it while this gets fixed upstream:
None of these are good substitutes for correct notification routing. But option 1 in particular was reliable enough for us to ship with while waiting on a fix. Worth filing this as a regression check for the Agent Teams feature too -- the nested completion routing is exactly what makes multi-level coordination fail silently rather than loudly.
Adding a data point from a different platform, plus a concrete request, because this limitation shapes how we can build multi-level agent trees today.
Environment
CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH=4; custom subagent types that have the Agent toolWhat we run
A main conversation spawns several "head" subagents (depth 1), and each head spawns its own children (depth 2–4) for a sub-stream of work. The heads write the children's briefs, accept their results, and hand a synthesis up to main.
What the docs say today
From https://code.claude.com/docs/en/sub-agents:
We can't tell which branch applies to conversations started by the VS Code extension. The 2.1.281 release notes list the extension together with
--input-format stream-jsonsessions. That suggests the "doesn't wait" branch, but we have not confirmed it.Why it hurts multi-level trees
Background children are unsafe for us because their results can land in the main conversation instead of the head that owns them. So our heads spawn children in the foreground only. Measured on one of our runs (29.09, 2.1.28x):
What would help (any one of these)
waitForBackgroundChildren. With it, a launching subagent does not finish, and does not send its own completion upward, while it still has tracked background children. This is the interactive behavior quoted above, made available to IDE and SDK sessions.SendMessageat the child's next tool boundary, or let the parent receive messages while it waits. Mid-flight facts from children could then change the parent's decisions. Today they arrive only after the child has already finished.Minimal scenario
run_in_background: true. C runssleep 120in Bash and then writes a marker file.sleep 20in a loop) until it appears.Expected, per the requests above: in variant B, H receives C's completion, and H's own completion reaches main only after that. In variant A, C's completion goes to H's caller, not to an unrelated top-level turn.
Possibly related: #75043.
Data point on 2.1.295 (current
latest), macOS 27.0.1, Agent SDK /--input-format stream-jsonsession driven by a third-party host, Opus 5.5. It answers the variant A/B question in the comment above and narrows what is still broken.Shape: main → 5 background
general-purposesubagents (depth 1) → each launches background workers withrun_in_background: true(depth 2; each worker'smeta.jsonhasparentAgentIdset to its depth-1 parent andspawnDepth: 2).1. A stopped depth-1 subagent is re-woken by its own background Bash task, but not by its own background Agent child. Two depth-1 subagents each started a background Agent worker plus a background Bash watcher, then ended their turn. Each was later resumed by the Bash task's
[SYSTEM NOTIFICATION], delivered into its own transcript. Their Agent workers' completion notices went only to the main session (the worker's task-id appears 0 times in the parent transcript and 2 times in the main one), and neither parent ran again after that. After its Bash wake, one parent said "The worker's final report has not arrived yet; I'll resume on its notification." Its worker had finished a minute earlier.2. Variant B (parent still mid-turn) works; variant A (parent already stopped) loses the notice. Two depth-1 subagents stayed in their turn with foreground Bash polls. Each received its worker's completion as a
queued_commandattachment at the next tool boundary and finished normally; the same notice was also enqueued in the main session. Three depth-1 subagents that had ended their turn before their worker finished never received the notice and never resumed on their own.3. The parent's completion note counts Bash children but not Agent children. One depth-1 subagent stopped twice while its Agent worker was running:
4. Item 1 of #75043 looks fixed here. A depth-1 subagent's
Agentcall withrun_in_background: falseblocked and returned the child's hand-back as its tool result.The bare case also reproduces with
claude -p --model sonneton 2.1.295 in 2 of 2 attempts: a background middle agent launches a background grandchild and ends its turn; the grandchild's notice lands in the main session and the middle agent never resumes.The docs sentence "In non-interactive mode and the Agent SDK, the launching subagent doesn't wait" covers routing to main. However, the Agent tool result inside the subagent still says "You will be notified automatically when it completes", and the note in item 3 says the parent has no live children while one is running. Since a stopped subagent is already re-woken for its own Bash tasks, delivering its Agent children's notices the same way would match both messages.