Don't set subagentPromptCacheTtl to 1h to fix subagent cache misses
Setting subagentPromptCacheTtl to 1h pays the higher cache-write rate on every short helper request to rescue the few that idle past five minutes; the real leak is the parent's cache going cold while it blocks on a child, which promptCacheTtl fixes.
The official documentation describes the mechanism this relies on. The technique itself is community practice.
What this costs you if it is wrong
The 8.6% figure is one unreplicated community measurement, not an Anthropic finding, and the mechanism is billing-dependent: on a Claude subscription within plan usage the main conversation already defaults to a one-hour cache, so the parent-goes-cold story mostly applies to API-key, cloud-provider, and usage-credit billing. Both settings and both environment variables require Claude Code v2.1.242 or later, so on an older client the prescription is unavailable and you are stuck with the defaults. Neither setting can delete work or leak data; the worst case is paying what you already paid.
Your /usage line shows a poor cache hit ratio and a pile of misses, and you spawn a lot of subagents. The docs say subagents get a five-minute cache TTL, your subagents plainly run longer than five minutes, and there is a setting named subagentPromptCacheTtl that takes 1h. The inference writes itself.
Resist it. The chain of reasoning is sound at every step and still lands on the wrong knob.
Why the obvious fix misfires
subagentPromptCacheTtl does not govern subagents. It governs a bucket that the docs call "everything else": subagents, workflows, in-process teammates, forks, compaction, and session titles. Most of the traffic in that bucket is short helper work that never idles at all — a session title is one request. Setting the bucket to 1h pays the one-hour cache-write premium on every one of those to rescue the minority that actually cross the five-minute gap.
The docs say the shape of this out loud without putting a number on it: the one-hour TTL "costs more on short bursts of work that never idle past five minutes, where the higher write rate applies and the longer cache lifetime goes unused." That sentence is describing your subagent bucket.
The leak that is actually worth chasing
While a subagent runs, the parent sits idle. It sends nothing. Every request the child makes builds the child's own prefix, which is separate — the docs are explicit that a subagent's first request does not read the parent's cache, because the two prefixes differ.
The parent's content survives this fine. From the parent's side, the call and its result just append; the prefix stays intact. What does not survive is the parent's TTL clock, which has been running the whole time nothing refreshed it. If the parent's cache is on a five-minute lifetime and the child takes nine minutes, the parent's entire context is re-written at cache-write rates the moment the child returns — and the bigger the parent's context, the more that costs.
Whether that can happen to you depends on how you are billed:
| Billing | Main conversation default | Subagent bucket default | Does the parent go cold during a long child? |
|---|---|---|---|
| Claude subscription, within plan usage | One hour | Five minutes | No — the hour covers the gap already |
| Usage credits, API key, or cloud provider | Five minutes | Five minutes | Yes, on any child that outlasts five minutes |
So on an API key the fix is promptCacheTtl set to 1h, not subagentPromptCacheTtl. The docs say this directly for that billing case, independent of subagents. If you are on a subscription that has tipped over into usage credits, the main conversation silently drops to the cheaper five-minute lifetime — same exposure, and the same fix.
{
"promptCacheTtl": "1h",
"subagentPromptCacheTtl": "5m"
}Check your version before you write either line. prompt-caching says "Both settings and both environment variables require Claude Code v2.1.242 or later" — that covers promptCacheTtl, subagentPromptCacheTtl, CLAUDE_CODE_PROMPT_CACHE_TTL, and CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL. Below v2.1.242 you cannot choose a TTL for either bucket at all: you get the defaults in the table above, which means the parent-goes-cold exposure is real on API-key billing and there is no knob to close it. Upgrading is the fix; there is no settings spelling that works instead.
If you want one specific subagent on an hour
You do not have to move the whole bucket. A single subagent can carry its own lifetime in frontmatter, which is the right reach when one long-running agent is the problem and session titles are not:
---
name: deep-researcher
description: Long multi-step research passes
experimental:
cacheTtl: 1h
---Two constraints on this. It requires Claude Code v2.1.248 or later, and Claude Code ignores a 1h there while a Claude subscription is drawing on usage credits. It also sits below both environment variables and both settings in the precedence order, so a bucket-wide setting still wins over it.
What is unverified here
The mechanism above is documented. The magnitudes are not.
The direction of this tip comes from a single community report on the Claude Code issue tracker: a profiling run over roughly 95 sessions and 1,800 subagents that measured a blanket 1h for the subagent bucket as 8.6% worse in total spend, with a head-1h/tail-5m split still 1.3% worse, and a median parent idle gap of about nine minutes. Its author models a −13.6% improvement from writing the parent context at 1h before dispatch and moving per-invocation data past the cache breakpoint.
Treat all four numbers as one person's unreplicated arithmetic on their own workload. The issue is open with no Anthropic response, the −13.6% is a model of a fix rather than a shipped result, and their conclusion that "no knob exists" for the parent-side fix overlooks promptCacheTtl — which is doing something very close to what they proposed.
What would settle it for your workload, since the answer is workload-dependent and mine or theirs does not transfer:
- Confirm which lifetime your writes are actually getting.
claude -p "hello" --output-format jsonreportsusage.cache_creationsplit intoephemeral_1h_input_tokensandephemeral_5m_input_tokens. - Run a representative subagent-heavy stretch of work with
FORCE_PROMPT_CACHING_5M=1, then the same work with your candidate setting, and compare thePrompt cache (main)line in/usage— hit ratio, miss count, warm state. Requires v2.1.251 or later. - Across a team, the OpenTelemetry exporter reports cache read and creation tokens per user and session, which is the only way to see this at a scale where a few percent is legible above the noise.
Until you have run step 2 on your own sessions, the defensible position is the cheap one: leave subagentPromptCacheTtl alone, set promptCacheTtl to 1h if you are billed by API key or credits, and spawn fewer long-running subagents from an already-enormous parent context.
Checkpoint
- Which setting governs the cache lifetime of the parent conversation that is blocked while a subagent runs?
Show answerAnswer
promptCacheTtl— the main conversation's setting, notsubagentPromptCacheTtl. The parent is still a main conversation while it sits blocked, so the child's bucket has nothing to do with its lifetime. On usage credits, an API key, or a cloud provider that bucket defaults to five minutes, which is exactly where the parent-goes-cold exposure lives; on a Claude subscription within plan usage it already defaults to an hour. Either way the setting only exists on v2.1.242 or later — below that there is no spelling that works instead, and upgrading is the fix. - Why does moving the whole "everything else" bucket to
1hcost more than it saves, when subagent work genuinely does exceed five minutes?Show answerAnswer
Because it is not a subagent setting.
subagentPromptCacheTtlgoverns the bucket the docs call "everything else" — subagents, workflows, in-process teammates, forks, compaction, and session titles — and most of that traffic is short helper work that never idles at all; a session title is one request. Moving the bucket pays the higher one-hour cache-write rate on all of it to rescue the minority that cross the five-minute gap, which is the docs' own warning that the hour "costs more on short bursts of work that never idle past five minutes." The one number attached to it, 8.6% worse total spend, is a single unreplicated community profiling run — trust the mechanism, not the magnitude. If one long-running agent is the real problem, putcacheTtl: 1hunderexperimentalin that agent's frontmatter instead of moving the whole bucket — that needs v2.1.248 or later, and Claude Code ignores it while a Claude subscription is drawing on usage credits. - On your billing arrangement, does the parent's cache actually expire during a nine-minute child — and what command tells you which TTL your writes are getting?
Show answerAnswer
Your billing decides the first half, and only you can look it up: on a Claude subscription within plan usage the main conversation defaults to an hour, so a nine-minute child does not strand it; on usage credits, an API key, or a cloud provider both buckets default to five minutes and the parent's entire context is re-written at cache-write rates the moment the child returns. Watch the boundary — a subscription that has tipped over into usage credits silently drops to the five-minute lifetime with nothing changing on your side. For the second half,
claude -p "hello" --output-format jsonreportsusage.cache_creationsplit intoephemeral_1h_input_tokensandephemeral_5m_input_tokens, which tells you the lifetime your writes are actually getting rather than the one you think you configured.