Agent SDK subagents have prompt caching disabled by default (enablePromptCaching: false)
Summary
Subagent requests spawned via the Agent tool have enablePromptCaching hardcoded to false, causing all subagent API calls to miss prompt caching entirely. This results in significant unnecessary cost for tools, system prompts, and conversation history being sent as uncached input on every request.
Root Cause
In the CLI bundle, the main REPL query path correctly defaults to enabling prompt caching:
// Main REPL path (line ~6108 in cli.js):
let S = w.enablePromptCaching ?? YWq(w.model) // YWq() returns true unless env var disabled
However, subagent internal queries explicitly default to false:
// Subagent path:
enablePromptCaching: z.enablePromptCaching ?? false // hardcoded false
This means:
- Main CLI (Opus):
enablePromptCaching→undefined→ falls through toYWq()→true✅ - Subagent (Sonnet/etc):
enablePromptCaching→undefined→ falls through tofalse❌
Impact
From real proxy logs analyzing 104 requests in a single session:
| Request Type | Count | Cache Behavior |
|-------------|-------|---------------|
| Main CLI (Opus) | 50 | ✅ Cache breakpoints injected, cache hits observed |
| Subagent (Sonnet) | 54 | ❌ Zero cache breakpoints, all input uncached |
Subagent requests typically carry ~7,000+ tokens of tools and system prompt that are identical across requests. Without caching, these are billed as full uncached input every time.
Cost example: 54 subagent requests × 7,013 uncached tokens each = ~378,000 wasted uncached input tokens per session.
Proposed Fix
Change the subagent default from false to use the same YWq() check as the main REPL:
// Before:
enablePromptCaching: z.enablePromptCaching ?? false
// After:
enablePromptCaching: z.enablePromptCaching ?? YWq(model)
This would make subagents respect the same environment variable controls (DISABLE_PROMPT_CACHING, DISABLE_PROMPT_CACHING_SONNET, etc.) while enabling caching by default — consistent with the main CLI behavior.
Environment
- Claude Code version: 2.1.63
- Agent SDK version: 0.2.63
- Provider: AWS Bedrock (but issue applies to all providers)
- Analysis method: Reverse proxy intercepting API requests, confirmed zero
cache_controlblocks in subagent request bodies
Workaround
Currently using a local reverse proxy that injects cache_control breakpoints into subagent requests that lack them. This works but adds operational complexity. A one-line fix in the CLI would eliminate the need for this workaround.
7 Comments
Same! I have no idea why they don't fix it. I got 400$ in a single thread with 10 agents. Normally it's 10 times cheaper.
Thanks for the detailed investigation. We looked into this and the code path you identified isn't the one subagents use, so changing that default wouldn't affect what you're seeing. The subagent path does enable caching by default.
That said, zero
cache_controlmarkers across 54 requests is worth understanding. One possibility is that cache markers depend on the shape of the system prompt, so a custom subagent system prompt may not get them even when caching is on.To help narrow it down, could you share:
Thanks for looking into this, @ashwin-ant.
To answer your questions:
~/.claude/agents/)My reverse proxy logs show the issue affects all Agent tool subagents, not just custom ones. The distinguishing factor is the User-Agent / code path:
pre=0means the Agent SDK sent zerocache_controlmarkers. My proxy then injects them (2bp), and caching works perfectly — proving the API supports it, the SDK just isn't sending them.I built a Go reverse proxy specifically to work around this. It intercepts all requests and injects
cache_controlbreakpoints for requests that lack them:https://github.com/KevinZhao/claudecode-bedrock-proxy
See the README section on Agent SDK caching for detailed analysis and log evidence.
Related lever for reducing subagent token pressure, adjacent to the caching fix: let subagents load a different set of CLAUDE.md files than the main session.
Today subagents inherit the full user-scope CLAUDE.md from
~/.claude/plus any project CLAUDE.md in cwd. With substantial user-scope customization, that's easily 10-20k tokens of preamble per spawn, most of which a typical research or edit subagent doesn't need.Possible shapes:
inheritClaudeMd: 'none' | 'project' | 'all'in the agent definition YAMLComplementary to the caching fix, not a substitute. Caching reduces cost when breakpoints exist; scoping reduces cost by not sending the bytes at all.
Has there been any progress on this issue from the claude team? This seems like quite a costly bug.
@ashwin-ant Kevin provided a detailed report that caching is not working on subagents in the sdk. we have evidence of the same behavior in our internal coding agent used by 1k developers on the latest SDK. could you please provide a further clarification or a statement on this?
Here is my approach to subagent cache enabling proxy.
https://gist.github.com/meteozond/a9d1e10691075786a1769a595bcc3fc3
It handles
<cache></cache>section inside prompt.