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

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

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)

  1. On idle, auto-forward the teammate's final assistant text to its spawner (the same way Agent tool results surface a subagent's final text for cwd-scoped/background subagents), or
  2. 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.

View original on GitHub ↗

3 Comments

klueless-io · 22 days ago

Still present on 2.1.226 — and the SendMessage recovery 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) and mac-mini-m4. Also matches #80727.

The regression

Both this issue (6/6 recovery) and #80727 report that explicitly pinging the agent via SendMessage recovers 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).

Agent(name: "probe", model: "haiku", run_in_background: true,
      prompt: "Run exactly one bash command: `echo probe-ok`.
               Then return, as your entire final message, this single line: PROBE-DONE")

The agent runs the command, writes PROBE-DONE as 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

  • Not model-dependent — Opus and Haiku fail identically (#80727 was fable-5)
  • Not prompt-dependent — 600-word multi-step prompts and the two-step probe above fail identically
  • run_in_background: false is not a workaround — it does not run synchronously; it returns "spawned… will receive instructions via mailbox" and fails the same way
  • Reproduced by a second, unrelated session on the same machine, and by a session on the second machine

ID resolution — extending #80727

TaskList returns "No tasks found" while agents are running. TaskOutput returns "No task found with ID" for the bare name, for name@session-<id> from the spawn result, and for the short t******** slug that TaskStop itself reports. TaskStop <bare-name> resolves instantly and reports task_type: "in_process_teammate".

So the name resolver behind TaskStop works, while the one behind TaskList / TaskOutput does not see these agents at all.

Ruled out locally

  • A hanging SubagentStop hook — ours curls a dead port and fails in 0.01s, connection-refused, 5 timed runs, no variance
  • Mixed npm/native install — single native binary, installMethod: native, no npm global
  • Machine-specific config — both machines carry identical hook sets and both fail

Workaround that does work

The subagent's transcript is written to disk regardless. The last assistant text block is the return value:

~/.claude/projects/<project-slug>/<session-id>/subagents/agent-a<name>-<hash>.jsonl

Note the a prefix — an agent named probe becomes agent-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 Workflow scripts, which are built on the same agent() 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.

janahanr-corvidai · 19 days ago

Confirming this on Linux / v2.1.226, and adding two details I haven't seen reported elsewhere in this cluster.

Environment

  • Claude Code 2.1.226 (native installer), Linux 6.8, running under tmux
  • CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" in ~/.claude/settings.json
  • teammateMode: "tmux" set, but not honouredTaskStop reported in_process_teammate and the team config's lead row shows "backendType": "in-process"
  • 4 agents spawned via the Agent tool with a name (2 implementers, 2 verifiers), run_in_background left at default

Reproduction

0 of 4 agents spontaneously returned a report. All four emitted repeated {"type":"idle_notification","idleReason":"available"} with no payload. Recovery via SendMessage:

| Agent | Outcome |
|---|---|
| impl #1 | Never delivered. 4 idle pings, 2 explicit SendMessage requests, 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.json lists only the lead in members, despite inboxes/ having been created for all four teammates:

~/.claude/teams/session-44c52780/
├── config.json          → "members": [ team-lead ]      ← only one entry
└── inboxes/
    ├── team-lead.json
    ├── <teammate-1>.json
    ├── <teammate-2>.json
    ├── <teammate-3>.json
    └── <teammate-4>.json                                 ← all four exist

Inboxes are created but membership is not recorded. This looks like a concrete mechanism for the ListAgents omission in #85764 — the agents can be addressed (the TaskStop name resolver finds them instantly) while any enumeration driven off members sees 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 a name. The team store keeps no transcripts at all — only config.json and inboxes/. And inboxes/team-lead.json contained 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 — name is the switch

Same session, same everything, name omitted:

| | with name | without name |
|---|---|---|
| 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/TeamDelete and 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 named Agent spawn 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 Agent tool'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 when name routes to the teammate path, or the teammate's final text should be delivered to the lead on idle rather than dropped.

mimkorn · 18 days ago

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 name to the Agent tool switches the spawn from the background-agent path to the teammate path, so the agent emits idle_notification through the team inbox instead of a task-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, that name should only provide addressability without changing the spawn path or result delivery, would fix this case too.

Showing cached comments. Read the full discussion on GitHub ↗