SendMessage to a completed subagent doesn't reuse the previous prompt prefix (cache miss on every resume)

Open 💬 0 comments Opened Jul 12, 2026 by vetaljanos

Version: 2.1.206 and 2.1.207, macOS arm64

I run Claude Code against a local llama.cpp behind an Anthropic-compatible proxy and
noticed that KV cache reuse collapses every time the coordinator sends a SendMessage to
a subagent that has already finished (in one session prefix reuse dropped from ~9300 to
~3100 tokens). I diffed the outgoing /v1/messages requests and the resumed request is
not a prefix extension of the agent's last request. Same thing happens against the real
API, see usage numbers at the bottom.

Two separate differences before the newly appended coordinator message (system prompt
and tools are byte-identical):

  1. The injected "Available agent types for the Agent tool: ..." reminder is gone after

resume. Depending on timing it shows up either as a standalone {"role":"system"}
message or as a <system-reminder> appended to the content of the last tool_result.
Either way it's in every request of the original run, but it's not written to the
subagent transcript (~/.claude/projects/<project>/<session>/subagents/agent-*.jsonl,
grep finds 0 occurrences), so the rebuilt conversation doesn't have it. Everything
after that point is a cache miss.

  1. tool_result blocks of parallel tool calls come back in a different order. With 4

parallel Reads it's intermittent, with 8 it happened in 3 out of 3 runs on 2.1.207,
different permutation each time (1,2,3,4,5,6,7,8 -> 1,4,3,2,5,6,7,8, 2,1,4,6,5,3,7,8,
1,3,4,7,5,6,2,8). The JSONL on disk has them in the original order, as sibling user
messages whose parentUuid each points to its own tool_use, with timestamps identical to
the millisecond — the reorder appears when the chain is reconstructed.

Repro

Directory with small files alpha.txt ... delta.txt (8 files makes symptom 2 fire every
time):

claude -p --allowedTools "Read,Agent,SendMessage" \
'Follow these steps exactly, in order:
1. Use the Agent tool (subagent_type: general-purpose) to launch ONE agent with this
   exact task: "Read the files alpha.txt, beta.txt, gamma.txt and delta.txt in the
   current directory. Issue all four Read tool calls in parallel in a single response.
   Then reply with a one-line summary of each file."
2. Wait for that agent to fully complete and read its result.
3. AFTER it has completed, use SendMessage to send this follow-up to that same agent:
   "What exactly was the fact stated in beta.txt? Quote it."
4. Report the agent'"'"'s answer verbatim, then stop.
Do NOT read the files yourself. Do not launch more than one agent.'

Capture the two subagent requests around the resume with any logging proxy. In my
2.1.207 run the last pre-resume request ends with

{"type":"tool_result","tool_use_id":"toolu_01JDung…","content":"1\tFile delta.\n2\tFact: …\n3\n\n<system-reminder>\nAvailable agent types for the Agent tool:\n- …"}

and the resumed request has the same block without the reminder:

{"type":"tool_result","tool_use_id":"toolu_01JDung…","content":"1\tFile delta.\n2\tFact: …\n3\t"}

Usage numbers (subagent on claude-haiku-4-5)

Same repro with the subagent pointed at claude-haiku-4-5-20251001, usage of the three
subagent requests:

| request | input | cache_read | cache_write |
|------------------------|-------|------------|-------------|
| initial (task) | 10211 | 0 | 10201 |
| after 4 parallel Reads | 11275 | 10201 | 1066 |
| resume via SendMessage | 11130 | 10201 | 919 |

After the second request the cached prefix is 10201+1066=11267, so the resume should
read ~11267. It reads 10201: the prefix diverges inside the last tool_result (the
dropped reminder above) and the tail gets re-billed as cache_write. ~1k tokens in this
toy example, but the reminder usually lands near the top of the conversation, so in
real sessions the miss covers most of the agent context. Sending a message while the
agent is still running doesn't go through transcript reconstruction and stays
prefix-identical — it's only the resume-after-completion path.

Expected: resume reuses the persisted transcript verbatim so everything before the
appended coordinator message is token-identical, same as messaging a live agent.

View original on GitHub ↗