[BUG] Teammate subagents of a forked-skill agent cannot reply to their parent; SendMessage to "team-lead" false-succeeds into an orphaned inbox

Status Open
Reported on v2.1.228
Maintainer reply None cached
Activity 5 comments · opened Aug 11, 2026

Summary

Subagents spawned as named mailbox teammates by a forked-skill background agent have no working reply path back to their parent — and one of the failure modes returns success: true while dropping the message. The parent deadlocks waiting for replies that can never arrive, with success signals on both sides.

Environment

  • Claude Code 2.1.228, macOS (darwin 25.5.0)
  • Model: claude-fable-5
  • Trigger: /code-review max --fix <path> (the skill runs as a forked background agent and fans out ~10 "finder" subagents via the Agent tool with name: set, mailbox-style)

What happened

The review agent spawned 10 named finder agents. Each spawn returned "Spawned successfully… will receive instructions via mailbox." All 10 finders ran to completion (their transcripts are on disk as top-level session .jsonl files) and each tried 3–4 times to deliver its findings. Every route failed:

  1. SendMessage({to: "main"}) → {"success":false,"message":"You are the main conversation — \"main\" addresses you. Send to a named agent instead."}

Each finder's context treats the finder itself as the main conversation, so the documented reply path for background subagents doesn't exist from inside this spawn mode.

  1. SendMessage({to: "code-review"}) (the parent's registered name) → {"success":false,"message":"No agent named 'code-review' is reachable."}

The forked-skill parent's name is registered only in the original session's registry; its own children can't resolve it.

  1. SendMessage({to: "team-lead"}) → {"success":true,"message":"Message sent to team-lead's inbox","msg_id":"…"}

This is the worst one: the parent is not running as a team lead and nothing ever drains a "team-lead" inbox, so the message is dropped after a success response.

Meanwhile the forward direction worked fine: the parent's SendMessage pings to the finders returned "Message sent to finder-X's inbox" and demonstrably woke all 10 finders within a minute. So the channel is one-way, and both directions report success.

Net effect: the parent sat idle "waiting for finder reports" indefinitely. From the outer session it looked healthy — the parent's spawn results, its pings, and the finders' team-lead sends all said success. I only found the findings by grepping the finders' transcripts and hand-delivering the payloads to the parent.

One extra footgun for anyone debugging the same thing: these teammate-style subagents do not write subagents/agent-*.jsonl files — their transcripts are top-level <uuid>.jsonl session files under ~/.claude/projects/<project>/, and the transcript does not contain the agent's own registered name.

Expected

  • A subagent should always have at least one working address for the agent that spawned it (parent name resolvable, or main routing to the spawner).
  • SendMessage must not return success: true for an inbox no consumer will ever drain — an unreachable recipient should be an error, like case 2.

Related

  • #74113 (background agents go idle without delivering their final SendMessage report) — similar symptom; in my case the sends themselves fail or false-succeed, so a re-ping cannot recover it.
  • #25135 (closed, stale) — same false-success-into-mismatched-inbox mechanism as case 3, reported against Agent Teams.

View original on GitHub ↗

3 Comments

jacwright · 2 months ago

Update with new evidence: the "team-lead" messages were not silently dropped after all — roughly 2 hours after the sends, every queued teammate message (idle notifications plus the full findings payloads) was delivered in a single batch to the main conversation of the session, wrapped as teammate-messages. Not to the parent agent that spawned the teammates, and long after those teammates had been stopped and the workflow manually recovered from transcripts.

So the more precise defect for case 3 is misrouting plus unbounded delay rather than a pure drop: SendMessage returns success, the parent (the only party waiting on the reply) never receives it, and the payload eventually surfaces to the top-level session hours later when it can no longer unblock anything. Cases 1 and 2 ("main" self-reference, parent name unresolvable) are unchanged.

vasyltretiakov · 2 months ago

Another instance of case 2 on a different spawn shape — plain background Agent dispatches, no Agent Teams and no forked skill. There's a second, separable failure here too: the spawner never received its child's completion notification.

Environment: Claude Code 2.1.229, Linux, model claude-opus-5[1m]. Subagents dispatched with the Agent tool using a custom subagent_type from .claude/agents/; no name: set on any spawn.

Shape: top-level session → background subagent A (custom type) → A dispatches its own background child B with isolation: "worktree".

1. B cannot address A (case 2, new spawn shape)

B finished its work and tried to report to A with SendMessage({to: "<A's subagent_type>"}), twice:

No agent named '<subagent_type>' is reachable.

B's toolset had no ListAgents, so it had no way to discover a valid address for A — and no way to learn that what it held was the wrong kind of identifier (an agent type, not a registered name). It retried, gave up, and reported the failure in prose inside its result.

2. B's completion notification did not reach A — it reached the top-level session

A's spawn result for B carried the usual contract: "You will be notified automatically when it completes… Do NOT Read or tail this file."

That notification never arrived. Checked against A's own transcript JSONL:

  • A's transcript contains exactly one <task-notification>, and it is not B's completion — it is a Monitor event with an unrelated task-id.
  • Meanwhile B's completion notification, carrying B's full result payload, was delivered to the top-level session — the grandparent, which did not spawn B and had no use for it.
  • A recovered B's findings only by reading <B-id>.output directly — the exact action the spawn result instructs agents not to take. A's transcript shows repeated reads of that path.

So for a subagent-spawned child the completion notification appears to be routed to the root session rather than to the spawner, while the spawner is told to expect it and told not to read the output file. The documented channel and the working channel are different ones, and nothing tells the spawner that.

Why this costs something even though nothing deadlocked

A did eventually get B's findings, so no deadlock — but only by violating its own instructions. And B had a working result channel (its return value) the entire time and could not tell: with no ListAgents and no address for its spawner in context, "can I reply to my parent?" is unanswerable from inside the child, so B spent turns on a route that cannot succeed and then reported a non-problem as a problem for A to reason about.

Suggested fixes

  • Route a child's completion notification to its spawner (in addition to, or instead of, the root session). Today the spawner is the one participant that both needs it and doesn't get it.
  • Surface the spawner's resolvable address in the child's context at spawn time, next to the existing guidance that the child's final message is its return value — that makes "return value vs. message" decidable from inside the child, which it currently isn't.

Related to #85160 (stable identifier as an address) and #81185 (restricted toolsets leaving no reply path).

(Revision note: an earlier edit of this comment withdrew the notification-routing claim as unverified. I then checked the spawner's transcript directly, and the claim holds — restored above with the evidence that establishes it.)

yongseek-choi · 1 month ago

Another instance of the same false-success shape, on a different platform and with a different reason the inbox has no reader.

Environment: Claude Code 2.1.263, Windows 11 Enterprise 26200.

What happened: SendMessage({to: "<peer-session-name>"}) returned success: true. Two and a half hours later the message is still sitting in ~/.claude/teams/session-<id>/inboxes/<name>.json with "read": false - and that session id no longer appears in the live session list. The recipient session had already exited.

So in this variant the inbox is orphaned not because the parent is not a team lead, but because the recipient session is gone. The delivery path does not appear to check recipient liveness at send time, and the sender receives no later signal that nothing will ever read the message.

Both pending inbox entries on this machine are "read": false; one of the two has a dead recipient.

The distinction that seems load-bearing: success: true currently means "written to a mailbox file", not "a live reader exists". A sender that waits on a reply therefore waits forever with a success signal in hand - which matches the deadlock described in the original report.

Suggestion: at send time, check whether the addressed session/agent is live (the runtime already tracks live sessions for ListAgents) and return success: false - or at minimum a distinct warning - when writing into a mailbox with no live reader. No agent named 'X' is reachable already exists as a failure mode for unknown names; a known-but-dead recipient could reuse it.

For contrast, the other two channels on this machine were reliable over the same period: background agent completion notifications arrived for 244 of 245 launched agents (the one outstanding was still running), and 73 of 73 continuations addressed by agent id succeeded. It is specifically the name-addressed mailbox path that fails silently.

Showing cached comments. Read the full discussion on GitHub ↗