Agent teams: SendMessage duplicates the message body ~3x per call (model-invented `content` field + result echo)
Summary
In agent-teams messaging, every free-text SendMessage moves the message body roughly 3 times:
message— the real payload.content— an undocumented top-level field the model invents on ~100% of free-text sends (1393/1396 in the corpus), containing a duplicate of the message body (plusrecipient, a copy ofto, andtype: "message").routing.content— the tool result echoes the full body back to the sender.
The documented input shape is {to, summary, message}; content/recipient/type are outside it, silently accepted, and never corrected. Net effect measured below: ~2.8x the tokens to move one message, ~two-thirds of it pure repeat.
Environment
- Claude Code 2.1.207 and prior versions, Windows 11 Pro
- Agent teams enabled (
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1, in-process teammates) - Model-independent — dirty on ~100% of free-text sends for every current model in the corpus: Fable 5 (683/683), Opus 4.8 (544/547), Sonnet 5 (130/130), Sonnet 4.6 (26/26), Haiku 4.5 (10/10).
Steps to reproduce
- Spawn a named teammate and have it send a couple of
SendMessagereports. - Read the session transcript JSONL. Every
tool_usewithname: "SendMessage"carries top-levelcontent(a copy ofmessage),recipient(a copy ofto), andtype: "message". - The parent-path
tool_resultrepeats the body underrouting.content.
Evidence (measured from real transcripts, one machine)
Method: byte lengths of the actual fields from transcript JSON; token figures are ~bytes/4 (same convention throughout). Ratios are tokenizer-independent because the duplicated fields are the same text as message.
Per-message breakdown (923 matched send+result pairs):
| Component | ~tokens | × original |
|---|--:|--:|
| Original send (to+summary+message) | 547k | 1.00× |
| + invented content (duplicate body) | 466k | +0.85× (output) |
| + recipient + type | 4k | +0.01× (output) |
| + echo (routing.content) | 497k | +0.91× (context) |
| Total on the wire | 1.51M | 2.77× |
It is model-generated, not harness-injected. In 128 calls, content is not a byte-copy of message but a shorter, independently-worded paraphrase of it (a mechanical duplicator cannot paraphrase). Joining tool_use → tool_result by id, the echo (routing.content) matches message in 157/157 divergent cases and content in 0 — i.e. the router uses message; the invented content is unused.
Single-day scale: one busy agent-teams day = 184 sends → ~180k redundant tokens (~80k wasted output from the invented fields + ~98k context from the echo).
Why passive fixes don't reach it
A fresh Sonnet-5 session with the accepted schema written into CLAUDE.md still emitted the full junk envelope. A PreToolUse hook that warned it (each call) its extra fields were being stripped was seen and dismissed as a false positive — the model asserted its call was clean {to, summary, message} from its self-model, and only retracted after being directed to re-read its own tool_use block. So docs and per-call warnings do not reliably correct it.
Expected behavior
- Free-text
SendMessageaccepts only{to, summary, message}; extra top-level fields are not silently accepted, or are prevented at the tool-call layer so they are never generated. - The result returns a minimal ack rather than echoing the full sent body back to the sender (who already has it).
Impact
Two compounding harms:
- Token cost. The invented
contentfield is pure wasted output at ~0.85× extra per send; the echo adds ~0.9× more as input/context. A linear, per-message tax that grows with the number of agents — one busy agent-teams day here was ~180k redundant tokens.
- Context-window pressure. Every message carries its body ~twice and each result repeats it a third time — ~2.8× total, ~two-thirds pure repeat — filling agent context with duplicated text and forcing earlier compaction. In long-running multi-agent teams that means lost history and degraded coordination, independent of the dollar cost.
Related, lower-priority observations
- Protocol replies (shutdown/plan-approval) are frequently emitted with structured fields flattened to top level plus the same junk fields.
idle_notificationdeliveries re-invoke the parent for zero content and aren't suppressible (dozens seen in one session).- PreToolUse
additionalContextis delivered in parent sessions but not in subagent contexts (blocks per-call feedback to subagents).
Happy to share the parser scripts and raw (anonymized) transcript samples.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗