Resuming a subagent via SendMessage drops injected role:"system" messages, invalidating the cached prefix (943k tokens in one session)
Summary
When a subagent is resumed after the coordinator sends it a message (SendMessage → "The coordinator sent a message while you were working: …"), Claude Code rebuilds the subagent's message history from the persisted transcript and drops every injected role: "system" message. Because those messages sit early in the conversation, the outgoing payload diverges from the cached prefix near its start, and the entire conversation region is re-created.
Observed in one 4-hour session: 8 occurrences, 943,213 tokens of avoidable cache creation, 88% of all in-conversation re-creation across 27 subagents.
Version: 2.1.233 · Platform: darwin 25.6.0 · Entrypoint: cli
Evidence
Captured with OTEL_LOG_RAW_API_BODIES=file:<dir> (raw request payloads, not the transcript — the transcript cannot show this, since the mutation is in the outgoing payload only).
Every request whose final message is "The coordinator sent a message while you were working: …", and the count of role:"system" messages in that same payload:
| time | prev nmsg → nmsg | first diverging index | depth below tail | cache_read | cache_creation | role:"system" count |
|---|---|---|---|---|---|---|
| 21:55:07 | 74 → 52 | 4 | 69 | 10,835 | 84,406 | 1 |
| 22:03:17 | 100 → 86 | 54 | 45 | 10,835 | 98,127 | 1 |
| 22:08:39 | 122 → 112 | 88 | 33 | 10,835 | 109,759 | 1 |
| 22:19:08 | 50 → 36 | 4 | 45 | 10,835 | 85,102 | 1 |
| 22:43:37 | 179 → 122 | 4 | 174 | 10,835 | 154,893 | 1 |
| 00:04:24 | 203 → 138 | 4 | 198 | 10,835 | 168,966 | 1 |
| 00:33:06 | 140 → 96 | 4 | 135 | 10,835 | 152,008 | 1 |
| 01:38:52 | 107 → 74 | 4 | 102 | 10,835 | 107,127 | 1 |
For comparison, across the same session's 868 ordinary deep subagent requests (nmsg > 40), the role:"system" count has median 26, max 116. On the resume path it is 1, every time.
read collapsing to exactly 10,835 is the signature: that is the byte-identical tools + system[1..2] prefix shared by every subagent launch. Everything after it is re-created.
Mechanism
At index 4 the message is replaced rather than merely shifted. Before:
{"role":"system","content":"Available agent types for the Agent tool:\n- claude: Catch-all for any task…"}
After the resume, index 4 is an assistant message (thinking, text, tool_use, tool_use) — the injected system messages are gone and everything below shifts up, so the role at that index changes. The message count drops correspondingly (e.g. 179 → 122).
The dropped messages are the harness's own injected context (available agent types, deferred-tool notices, <total_tokens> budget notices). They are present in the payload on every normal turn and absent on every resume turn.
Impact
- Cache prefix diverges 33–198 messages deep, so the whole conversation region is re-created.
- 943,213 tokens in a single session, on 8 events.
- Scales with subagent conversation length: the deepest case (203 messages) cost 168,966 tokens in one call.
- Silent — nothing surfaces it. The transcript is unaffected, so transcript-based cache tooling cannot see it either.
Expected
Resuming a subagent should reproduce the prior payload prefix byte-for-byte, injected role:"system" messages included, so the cached prefix survives and only the new coordinator message is appended.
Reproduction
export OTEL_LOG_RAW_API_BODIES=file:/tmp/bodies- Start a session that spawns a subagent and lets it run long enough to accumulate ~50+ messages.
- Use
SendMessageto message the running subagent. - Compare the resumed request payload with the previous one from that subagent: count
role:"system"entries in each, and comparecache_read_input_tokensbefore and after.