In-process teammate's final text is discarded on idle — coordinator gets a payload-less idle_notification and must round-trip to recover the already-written report
Version: Claude Code 2.1.222 · macOS arm64 · CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
Summary
An in-process teammate (Agent spawned with a name) that finishes its task and ends its turn with plain final text delivers that text to nobody. The coordinator receives only a payload-less idle notification ({"type":"idle_notification","idleReason":"available"}). The completed report sits in the teammate's context; it arrives only if the coordinator explicitly messages the agent asking it to re-send via SendMessage.
Measured: six occurrences in one working day across two coordinator sessions (three research/forensics agents each). In all six cases the report was fully written before the agent went idle, and one "send your report via SendMessage" ping recovered it intact (6/6).
Why this bites
- The natural brief wording — "your final message is the report" — is a trap: the agent ends its turn with text, which for a named teammate lands nowhere. Nothing in the spawn flow or the agent's own view warns that plain final text is discarded.
- The idle notification carries no content and no indicator of whether the agent produced a deliverable, so the coordinator cannot distinguish "finished with a report waiting" from "went idle empty-handed" without a round-trip.
- Combined with delivery batching (#84494) the recovery round-trip can add tens of minutes per agent to a fan-out workflow.
Expected (either would resolve it)
- On idle, auto-forward the teammate's final assistant text to its spawner (the same way
Agenttool results surface a subagent's final text for cwd-scoped/background subagents), or - Include the final text (or at least a has-deliverable flag) in the idle notification payload.
Workaround
Briefs must end with an explicit "your FINAL ACTION must be SendMessage to <coordinator name> with the full report"; coordinators treat idle-without-report as a trigger for one deliver-now ping. Both are convention, not mechanism — each new agent brief can silently regress.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Still present on 2.1.226 — and the
SendMessagerecovery described in this report no longer works.Reproduced today (2026-08-08) on two machines, independent native installs, both Claude Code 2.1.226, macOS arm64 —
roamy(MacBook Pro M4) andmac-mini-m4. Also matches #80727.The regression
Both this issue (6/6 recovery) and #80727 report that explicitly pinging the agent via
SendMessagerecovers the report. On 2.1.226 it does not. I asked an idle agent to restate its result; its transcript shows it complied and produced the text — and that reply was also never delivered. The failure has moved from "the initial report is dropped" to "nothing the agent emits is delivered", which removes the documented workaround.Minimal repro
No agent-teams env var set (see note at the end).
The agent runs the command, writes
PROBE-DONEas its final assistant text, then emits{"type":"idle_notification","idleReason":"available"}on a ~4-minute cycle. The parent receives nothing.Scope — 8 agents, 3 sessions, 2 machines, 100% failure
run_in_background: falseis not a workaround — it does not run synchronously; it returns "spawned… will receive instructions via mailbox" and fails the same wayID resolution — extending #80727
TaskListreturns "No tasks found" while agents are running.TaskOutputreturns "No task found with ID" for the bare name, forname@session-<id>from the spawn result, and for the shortt********slug thatTaskStopitself reports.TaskStop <bare-name>resolves instantly and reportstask_type: "in_process_teammate".So the name resolver behind
TaskStopworks, while the one behindTaskList/TaskOutputdoes not see these agents at all.Ruled out locally
SubagentStophook — ourscurls a dead port and fails in 0.01s, connection-refused, 5 timed runs, no varianceinstallMethod: native, no npm globalWorkaround that does work
The subagent's transcript is written to disk regardless. The last assistant
textblock is the return value:Note the
aprefix — an agent namedprobebecomesagent-aprobe-<hash>.Caveat: if anything messaged the agent after it finished, the reply to that is now the last block, so scan all blocks rather than only the final one.
Impact
Anything depending on a returned agent value is silently lossy — full token cost paid, work completed, result discarded. That includes fan-out patterns and
Workflowscripts, which are built on the sameagent()primitive.A real security scan was lost this way and redone by hand; recovering the transcripts afterwards showed the agents had found 7 findings that the manual redo missed.
One note on environment
This reproduces without
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1, which this issue was originally filed under. The flag is not required to trigger it.Confirming this on Linux / v2.1.226, and adding two details I haven't seen reported elsewhere in this cluster.
Environment
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1"in~/.claude/settings.jsonteammateMode: "tmux"set, but not honoured —TaskStopreportedin_process_teammateand the team config's lead row shows"backendType": "in-process"Agenttool with aname(2 implementers, 2 verifiers),run_in_backgroundleft at defaultReproduction
0 of 4 agents spontaneously returned a report. All four emitted repeated
{"type":"idle_notification","idleReason":"available"}with no payload. Recovery viaSendMessage:| Agent | Outcome |
|---|---|
| impl #1 | Never delivered. 4 idle pings, 2 explicit
SendMessagerequests, no report ever produced || impl #2 | Recovered on request |
| verifier #1 | Recovered on request |
| verifier #2 | Recovered on request |
So 3/4 recoverable, 1 permanently lost on 2.1.226 — partway between the fully-recoverable behaviour originally reported here and the total-regression report in the comment above. All four completed their actual work correctly on disk; only the report channel failed, which is what makes it dangerous: it reads as "still thinking", not "output discarded".
New detail 1 — the team config never registers the teammates
~/.claude/teams/session-<id>/config.jsonlists only the lead inmembers, despiteinboxes/having been created for all four teammates:Inboxes are created but membership is not recorded. This looks like a concrete mechanism for the
ListAgentsomission in #85764 — the agents can be addressed (theTaskStopname resolver finds them instantly) while any enumeration driven offmemberssees nothing.New detail 2 — there is no disk fallback for a teammate that never delivers
The workaround of reading the subagent transcript off disk does not apply to in-process teammates.
~/.claude/projects/<slug>/<session-id>/subagents/contained transcripts only for agents spawned without aname. The team store keeps no transcripts at all — onlyconfig.jsonandinboxes/. Andinboxes/team-lead.jsoncontained 0 messages (the recovered reports were delivered live in-session, not persisted).So for the one agent that never responded to
SendMessage, the output is unrecoverable by any route. Worth noting since transcript-recovery is suggested as the reliable fallback in #71723 — it works for background subagents, not for teammates.Control experiment —
nameis the switchSame session, same everything,
nameomitted:| | with
name| withoutname||---|---|---|
| Spawn message | "will receive instructions via mailbox" | "Async agent launched… you will be notified automatically when it completes" |
| Transcript on disk | none | yes, under
subagents/|| Report delivery | never automatic | auto-returned, 3/3 |
Three unnamed agents were spawned and all three returned full reports automatically. Single variable, and it flips the outcome completely.
Why this may be surfacing for more people now
This machine ran 2.1.159 from 2026-05-31 until 2026-08-06, then jumped straight to 2.1.223 — skipping 2.1.178, which removed
TeamCreate/TeamDeleteand made every session carry an implicit team when the env var is set. Grepping the two binaries:| string | 2.1.159 | 2.1.223 |
|---|---|---|
|
single implicit team| 0 | 6 ||
TeamCreate| 8 | 3 |The env var had been set here since April and was harmless the whole time, because teams still required an explicit
TeamCreate. After 2.1.178 the same unchanged setting silently converts every namedAgentspawn into a teammate. Anyone who set that flag before June and upgrades across 2.1.178 gets this behaviour change with no config change on their side — which is likely to keep generating reports that look like "subagents suddenly stopped returning results".Suggestion
The mismatch is that the
Agenttool's own contract tells subagents their final text is the return value, while the teammate path discards exactly that. Either the spawn result should warn whennameroutes to the teammate path, or the teammate's final text should be delivered to the lead on idle rather than dropped.Confirming on 2.1.228, macOS. Three named background agents in one session, two different agent types, three unrelated tasks. All three signalled
{"type":"idle_notification","idleReason":"available"}with no payload, and all three had a complete report already written. One "send me your findings" ping recovered each of them intact. 3/3, matching your 6/6.The expensive part was not the round-trip. It was that my coordinator session came close to reporting that the investigations had returned nothing, because there is no signal separating "idle with a report waiting" from "idle having found nothing". The safe assumption and the natural assumption point in opposite directions.
Possibly worth linking from here: #71723 describes what looks like the mechanism. Passing
nameto the Agent tool switches the spawn from the background-agent path to the teammate path, so the agent emitsidle_notificationthrough the team inbox instead of atask-notification. All three of mine were named, and I had done that only for addressability. If it is the same root cause, then #71723's stated expectation, thatnameshould only provide addressability without changing the spawn path or result delivery, would fix this case too.