Agent teams: SendMessage duplicates the message body ~3x per call (model-invented `content` field + result echo)

Open 💬 1 comment Opened Jul 14, 2026 by pounceai-bit

Summary

In agent-teams messaging, every free-text SendMessage moves the message body roughly 3 times:

  1. message — the real payload.
  2. 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 (plus recipient, a copy of to, and type: "message").
  3. 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

  1. Spawn a named teammate and have it send a couple of SendMessage reports.
  2. Read the session transcript JSONL. Every tool_use with name: "SendMessage" carries top-level content (a copy of message), recipient (a copy of to), and type: "message".
  3. The parent-path tool_result repeats the body under routing.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_usetool_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 SendMessage accepts 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 content field 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_notification deliveries re-invoke the parent for zero content and aren't suppressible (dozens seen in one session).
  • PreToolUse additionalContext is 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.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗