[BUG] Nested subagent (depth-2) completion notifications route to root session and are discarded on 2.1.250 — regression of #75043 item 2

Status Open
Reported on v2.1.226
Maintainer reply None cached
Activity 1 comment · opened Aug 28, 2026

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:

  1. The worker notifications carried full <result> payloads (~23KB each) and were removed, not dequeued — the results were discarded without ever being delivered to anyone.
  2. The 01:58:22 record shows the "stops with no live background children of its own" guard is also blind to grandchildren: the orchestrator was reported completed to 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 all queue-operation records
  • agent-<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 completed signal

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗