[BUG] Nested subagent (depth-2) completion notifications route to root session and are discarded on 2.1.250 — regression of #75043 item 2
Environment
- Claude Code v2.1.250
- Linux / WSL2 (
6.18.33.2-microsoft-standard-WSL2) - CLI driven headlessly by a third-party host (bb 0.40.0), not the interactive TUI
- Models: Opus 5 root + subagents
Summary
Completion notifications for depth-2 agents (a subagent's own children) are enqueued to the root session instead of the subagent that spawned them, and are then discarded without delivery. The spawning subagent starves at its join point.
This is the same failure as item 2 of #75043, which was marked fixed in v2.1.233 and confirmed working by two reporters on 2.1.226 and 2.1.234. It reproduces cleanly on 2.1.250, so the fix has regressed or does not cover this entry point. Filing separately rather than reopening that thread; see references below.
Shape
Root session 8f2156be → orchestrator aa0360e9 (depth 1) → 3 workers (depth 2).
Item 1 of #75043 looks genuinely fixed at depth 1: the root spawned the orchestrator with run_in_background: false and the call blocked correctly. The orchestrator then spawned its 3 children; run_in_background is absent from the subagent-side Agent schema, so all three were forced async ("Async agent launched successfully").
Parentage is recorded correctly — each worker's subagents/agent-<id>.meta.json contains "parentAgentId":"aa0360e9cbe823af9","spawnDepth":2 — but the notification layer ignores it.
Evidence
Every queue-operation record in the transcript is stamped with the root sessionId, at both depths. The subagent transcripts contain only user/assistant/attachment records — zero queue-operation entries. Subagents have no queue to receive from.
01:58:22 enqueue aa0360e9cbe823af9 completed sess=8f2156be <- premature: all 3 children still live
01:59:32 enqueue a63cd4c5e7ac2fe49 completed sess=8f2156be <- worker, parent=aa0360e9
02:00:14 enqueue a1669a0bb2da7607c completed sess=8f2156be <- worker, parent=aa0360e9
02:01:02 enqueue a45fa3c4b23e235ae completed sess=8f2156be <- worker, parent=aa0360e9
02:01:03 remove a63cd4c5e7ac2fe49
02:01:03 remove a1669a0bb2da7607c
02:01:03 remove a45fa3c4b23e235ae
Two details worth flagging:
- The worker notifications carried full
<result>payloads (~23KB each) and wereremoved, notdequeued — the results were discarded without ever being delivered to anyone. - The
01:58:22record shows the "stops with no live background children of its own" guard is also blind to grandchildren: the orchestrator was reportedcompletedto root while all three of its children were still running.
Expected vs actual
Expected: a worker's completion notification is delivered to the agent that spawned it (parentAgentId), waking it.
Actual: it is enqueued against the root sessionId, then removed undelivered. The spawning subagent is never woken and never sees the result.
Impact
In this run the orchestrator stalled at its join point and began shelling out to poll its children's JSONL files by hand. The root session noticed, sent SendMessage ("Stop waiting — the worker notifications are not going to arrive"), then gave up and re-spawned all three research tasks itself at 01:59:51–02:00:22, duplicating work the original workers had already completed. The orchestrator eventually produced its report at 02:04:33, ~6 minutes late, entirely via the file-polling workaround.
So the practical cost is a stalled join, ~2x token spend on duplicated fan-out, and a hang that only resolves through manual operator/root intervention.
Possibly relevant to the regression
This is a headless/SDK-driven session rather than the interactive TUI. If the 2.1.233 fix routed notifications through a TUI-side path, it may not cover this entry point.
Full transcripts
Unredacted, at https://gist.github.com/zacharied/fe74645de1d8969f18549e8bb0ff0800
8f2156be….jsonl— root session, including allqueue-operationrecordsagent-<id>.jsonl— all 7 agent transcripts (depth 1 and depth 2)agent-<id>.meta.json— parentage metadata
The three workers' parentage records, verbatim:
{"agentType":"general-purpose","description":"Research C++ hot reload in Qt","toolUseId":"toolu_01UARN4BRsWNvbBfxdBQRizz","parentAgentId":"aa0360e9cbe823af9","spawnDepth":2}
{"agentType":"general-purpose","description":"Research PySide6 hot reload","toolUseId":"toolu_019dtWmqttnPHVQU2TA59K54","parentAgentId":"aa0360e9cbe823af9","spawnDepth":2}
{"agentType":"general-purpose","description":"Research OSS Qt apps and licensing","toolUseId":"toolu_01ALSMuyTng377PSDnVWQaVZ","parentAgentId":"aa0360e9cbe823af9","spawnDepth":2}
Each of those toolUseIds appears in the root session's queue-operation records above, and in no subagent transcript.
References
- #75043 — canonical thread; this is its item 2, declared fixed in 2.1.233
- #86485 — grandchild notifications route to root instead of spawning parent
- #86963 — nested background subagent never re-woken (WSL, 2.1.224); also documents the premature
completedsignal
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗