[BUG] Agent Teams mailbox: 5-62 min turn-boundary delays, lost final reports (idle_notification arrives instead), /clear queue leak, shutdown handshake never completes

Status Open
Reported on v2.1.206
Maintainer reply None cached
Activity 4 comments · opened Jul 10, 2026

Environment

  • Claude Code 2.1.206, macOS (darwin 25.6.0)
  • CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
  • Orchestrator session + teammates spawned via the Agent tool (tmux backend)

Summary

Four related delivery defects in the Agent Teams mailbox, all observed in one day across two sessions (a 5h orchestrator run and the diagnostic session that analyzed it). Together they force both sides of a team to act on stale state; in our case the orchestrator only avoided corruption by re-verifying git log/status before acting on any message.

1. Messages are delivered only at the receiver's turn end (measured 5-62 min delays)

When the receiving session is mid-turn (long builds, verification), incoming teammate messages queue until the turn ends. Measured deliveries (sent timestamp inside the idle_notification payload vs. wall-clock delivery into the transcript): 5, 6, 19, 21, 25 and 62 minutes. Both directions are affected; the teammate side repeatedly asked about things the lead had already resolved because the lead's answers were still queued.

2. Substantive final messages lost entirely while idle_notifications arrive

Reproduced 4 times in a single session: a spawned agent finishes, calls SendMessage with its full final report, and the recipient (team lead) receives only the automatic idle_notification. The report never arrives, at any turn boundary. The content was recoverable only by reading the teammate's transcript .jsonl from disk. The teammate's transcript shows the SendMessage tool call succeeding.

3. Queue leaks across /clear

An idle_notification generated by a teammate of the previous session (before /clear) was delivered into the new session 62 minutes after it was generated, with no marker distinguishing it from current-session traffic.

4. shutdown_request handshake never completes

Lead sends shutdown_request (tool result confirms delivery and returns a request id). Teammates process it but their shutdown_response never reaches the lead, and the teammates do not terminate: they go idle and emit more idle_notifications. Repeated with three teammates simultaneously; all three had to be killed via tmux kill-pane.

Expected

  • Documented delivery semantics (ordering, latency bounds) for teammate messages, or turn-interrupting delivery for substantive messages.
  • No message loss: an idle_notification should never overtake and replace the substantive message that preceded it.
  • Mailbox flush or epoch-tagging on /clear.
  • shutdown_request/shutdown_response handshake completing without manual pane kills.

Related

#47930 (idle-notification noise), #46691 (lead stall after teammates report back), #28627 (idle notifications rendered as Human turns) look adjacent but none covers message loss, turn-boundary latency, the /clear leak, or the shutdown handshake.

View original on GitHub ↗

3 Comments

greghughespdx · 3 months ago

Corroborating the turn-boundary delays here, with one extra data point that may help
localize the drain code: in our session the queued notifications did not flush at
ordinary turn boundaries at all - they flushed at the moment the NEXT agent was spawned.

Observed four consecutive times in one session on 2026-07-11 (Claude Code v2.1.207,
macOS, interactive tmux session, background subagents via the Agent tool with name
set). All times UTC; message timestamps are from the notification payloads, delivery
times are when they surfaced in the parent transcript (approximate to the turn):

| shutdown trio generated | delivered | delay | delivery trigger |
|---|---|---|---|
| 21:35:14 | ~21:46 | ~11 min | next Agent spawn |
| 21:53:14 | ~22:15 | ~21 min | next Agent spawn |
| 22:20:16 | ~22:37 | ~17 min | next Agent spawn (2 at once) |
| 22:49:27 | ~23:42 | ~53 min | next Agent spawn (3 at once) |

Delays tracked the gap between an agent's shutdown and the NEXT spawn, not turn
activity - several user turns and tool calls happened in between with nothing
delivered. This suggests the pending-notification queue is only drained by the
Agent-tool spawn path rather than by a per-turn poll.

Also matching this issue's "lost final reports" symptom: one background agent went idle
without delivering its final SendMessage report and needed an explicit re-ping to hand it
over (same behavior as #74113). And within a flushed batch, ordering was inverted -
an agent's idle_notification was delivered after its teammate_terminated.

oconnorjoseph · 2 months ago

Additional data point — lead→teammate direction, background Agent-tool spawns, 2.1.212 (in-process / default mode, not tmux)

Corroborating defects #1 and #2 from a separate deployment: a headless orchestrator (main) with background Agent-tool spawns, in-process/default backend, CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1. Two occurrences worth adding because they cover the lead→teammate direction and sharpen the "delivered only at turn end" framing.

  1. Idle subordinate not resumed by a single inbound message. The orchestrator sent a fully-specified task message via SendMessage to a subordinate that had already gone idle (idleReason: "available") after finishing its prior task. The message did not resume it: the next signal was another idle_notification, and the subordinate's working tree was byte-identical — zero progress. Only a second SendMessage ("checking in — did you receive this?") woke it, at which point it processed the FIRST message correctly (it had been in context the whole time). So for a parked/idle subordinate, a single inbound SendMessage does not reliably trigger a resume — a fresh second message is required to un-stick it. This is the headless/non-tmux analog of #34668's "press Esc to un-stick," but here no UI keypress exists.
  1. Busy→idle transition is not a flush point. Two SendMessage deliveries (a work assignment + an explicit nudge) arrived while the receiving agent was mid-turn. It finished its current work and transitioned busy→idle without draining either queued message — the busy→idle transition itself was not a delivery trigger. A THIRD message (sent later) resumed it, and it then drained the whole backlog in order — ~40 min after the first assignment, by which point the orchestrator had already re-done the assigned work inline. This matches defect #1 ("delivered only at the receiver's turn end") but sharpens it: for a subordinate that parks to idle, even the idle transition is not a flush point — only a subsequent inbound message is.

Compounding both: the idle_notification payloads replayed the agent's own last outbound summary rather than reflecting unread inbox state, so an idle ping looked like a re-ask and gave no signal that N messages were sitting undrained. This is the concrete "an idle_notification should never overtake and replace the substantive message that preceded it" symptom from your Expected section — a per-idle "N unread inbox messages" field would make the drop visible immediately (related: #74112, #47930).

Environment: Claude Code 2.1.212, background Agent-tool spawns (default/in-process mode), CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1. Inbound-deferral mechanism report: #50779. Direction-exact (lead→teammate) report: #34668 (closed stale).

huiwenshi617-creator · 19 days ago

Still present in 2.1.281 (macOS arm64, CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1, tmux split panes). The comment above found that held notifications flush when the next agent is spawned. Here is a repro, and a likely cause, for that "flush at next spawn" pattern.

Repro: retiring the last teammate

  1. Start a lead with agent teams enabled (tmux split panes).
  2. Spawn exactly one teammate.
  3. Have the lead send it {"type": "shutdown_request"}. The teammate approves.
  4. Leave the lead idle, or type a few messages to it. Each of those turns ends without delivering teammate_terminated.
  5. Have the lead spawn a new teammate. At the end of that turn, the held teammate_terminated, the shutdown_approved and the teammate's last message all arrive together.

The lead's own state already shows the exit before that: the pane is closed, the member is gone from ~/.claude/teams/<team>/config.json, and TaskStop on the teammate returns is not running (status: completed). Only the message to the model is missing. So a lead that waits for the notice keeps telling the user it is "waiting for the teammate to confirm".

How often (one lead session, one week)

  • 27 exit notices had an approval timestamp. 8 arrived more than 15 minutes after the approval; the longest took 15.5 hours. The other 19 arrived within 7 minutes.
  • The 8 late ones came in 6 deliveries. 5 of the 6 landed within a minute after the next teammate was spawned.

Likely cause

This is from reading the minified 2.1.282 bundle, so identifiers may differ.

  • The lead's inbox poller handles shutdown_approved ([InboxPoller] Found N shutdown approval(s)) as follows:
  • it kills the pane;
  • it removes the member from the team file and from the in-memory team;
  • it appends teammate_terminated to the pending inbox with status: "pending".
  • Pending items are delivered by the path that logs [InboxPoller] Session idle, delivering N pending message(s). That path, and the poll timer itself, run only while the lead's poller identity resolves. For the lead, it resolves only when the team has at least one teammate besides the lead.
  • So the step that removes the last teammate also turns off delivery of the notice it just queued. Spawning a teammate turns the poller back on, and the pending items go out at the next turn end.

Also seen, not explained

Once, with a live teammate still on the team, two ordinary messages from it sat undelivered for about 40 minutes while the lead was idle. An already-held exit notice was waiting with them. Then all of them arrived together, with nothing visible triggering it. We could not tell which delivery condition held them: an open dialog, a turn still marked running, or a queued prompt.

Related docs mismatch: the team folder name

The agent-teams docs say three things:

  • teams are stored under session- plus the first eight characters of the session ID;
  • the team config directory is removed when the session ends;
  • a session has exactly one team.

This was a long-running lead session whose process was apparently restarted (resumed) mid-conversation. We inferred the restart; we did not observe it. What we saw:

  • The transcript kept its original session ID, AAAAAAAA….
  • The live team folder is session-BBBBBBBB. Its config's leadSessionId is BBBBBBBB-…, an ID that matches no transcript.
  • A folder session-AAAAAAAA, created 16 days earlier with empty inboxes, still exists, although the session never ended.

So building the team path from the current session ID finds a stale team that looks valid. The reliable source was the @<team> suffix of agent_id in the Agent tool's spawn result.

Showing cached comments. Read the full discussion on GitHub ↗