Claude Prompt Cache Not Hitting — Cache Miss Fix (2026) | AI Error Hub
Anthropic Claude Prompt Caching Severity: Medium Cost issue

Claude Prompt Cache Not Hitting

You added cache_control but cache_read_input_tokens stays at zero. Root causes: content before the breakpoint under 1024 tokens (Sonnet/Opus) or 2048 (Haiku), breakpoint not on cacheable content type, or upstream content changed invalidating cache.

Error surface: Silent cache miss Category: Prompt Caching Diagnostic: usage.cache_read_input_tokens Last tested: Nov 15, 2026
Quick Fix (TL;DR)

Cache breakpoints only work if the content before AND including the breakpoint is at least 1024 tokens (Sonnet/Opus) or 2048 (Haiku). Put breakpoints at the end of stable, long content: system prompt, tools, or repeated document. Verify with usage.cache_read_input_tokens > 0 on the second request.

The Symptoms You're Seeing

Common signs of cache miss
Response usage always shows:
  cache_creation_input_tokens: 5423
  cache_read_input_tokens: 0
  → Cache is being WRITTEN every time, never read

Or:
  cache_creation_input_tokens: 0
  cache_read_input_tokens: 0
  → Cache breakpoint ignored — likely under minimum tokens

Costs match non-cached baseline
  → No cache hits happening at all

Only sometimes hits, unpredictable
  → Cache TTL expiring OR upstream content varies subtly

extra_headers not set:
  → prompt-caching beta header missing (older SDKs)

Minimum Token Requirements

ModelMinimum tokens before/at breakpoint
Claude Opus 4.61024
Claude Sonnet 4.51024
Claude Haiku 4.52048

The cumulative content from the start of your prompt up to and including the breakpoint must meet the minimum. Content AFTER the breakpoint doesn't count toward that budget.

What Actually Causes Cache Misses

45%
Cumulative content under minimumSmall system prompt + breakpoint = under 1024 tokens, silently ignored.
22%
Content before breakpoint changes between requestsEven a single character difference invalidates cache. Timestamps, user IDs, etc.
15%
Breakpoint on wrong content typeSome old SDK versions only respected cache_control on system/tools. Modern SDKs allow messages too.
10%
TTL expired (5 min ephemeral default)Requests spaced >5 min apart = fresh cache write each time.
8%
Model changed between requestsCache is per-model. Switching from Sonnet to Opus = new cache.

Fixes That Work (Tested Nov 2026)

1Verify Content Meets Minimum Tokens

Python · pre-flight token count
import anthropic client = anthropic.Anthropic() def verify_cacheable(system_content, model="claude-sonnet-4-5"): """Check content is above minimum before adding cache_control.""" minimums = { "claude-opus-4-6": 1024, "claude-sonnet-4-5": 1024, "claude-haiku-4-5": 2048, } # Use token counting API (free) count = client.messages.count_tokens( model=model, system=system_content, messages=[{"role": "user", "content": "probe"}] ) minimum = minimums[model] if count.input_tokens < minimum: print(f"WARNING: {count.input_tokens} < {minimum} — cache will not hit") return False return True # Only add cache_control when it will work system_content = load_large_system_prompt() if verify_cacheable(system_content): system = [{"type": "text", "text": system_content, "cache_control": {"type": "ephemeral"}}] else: system = system_content # plain, no cache overhead

2Correct Breakpoint Placement

Python · optimal breakpoint layout
# CORRECT: breakpoint on stable content at end of prefix response = client.messages.create( model="claude-sonnet-4-5", max_tokens=1024, system=[ {"type": "text", "text": LARGE_SYSTEM_PROMPT, "cache_control": {"type": "ephemeral"}} # ← cache this ], tools=STABLE_TOOLS_LIST, messages=[ {"role": "user", "content": [ {"type": "text", "text": LONG_DOCUMENT, "cache_control": {"type": "ephemeral"}}, # ← and this {"type": "text", "text": user_question} # varies per call ]} ] ) # WRONG: breakpoint on varying content messages=[ {"role": "user", "content": [ {"type": "text", "text": user_question, "cache_control": {"type": "ephemeral"}} # ← useless, question changes ]} ]
Cache prefix rule

Cache matches by exact prefix. If ANY byte before or at the breakpoint differs from a previous request, it's a miss. Put breakpoints AFTER stable content, BEFORE varying content. System prompt + tools + document = cache. User question + timestamp = don't cache.

3Verify Cache Hits After First Request

Python · cache hit rate monitoring
def log_cache_stats(response, request_num): u = response.usage if u.cache_creation_input_tokens > 0: status = "WRITE" elif u.cache_read_input_tokens > 0: status = f"HIT ({u.cache_read_input_tokens} tokens read)" else: status = "MISS (no cache activity)" print(f"Request {request_num}: {status} | " f"input={u.input_tokens} output={u.output_tokens}") # Expected pattern: # Request 1: WRITE (cache_creation_input_tokens: 5423) # Request 2: HIT (5423 tokens read at 10% cost) # Request 3: HIT (5423 tokens read at 10% cost) # If Request 2 shows WRITE again: prefix changed or TTL expired

Preventing This Error Going Forward

  • Count tokens before adding cache_control. Free API call, prevents wasted overhead.
  • Freeze content before breakpoint. No timestamps, session IDs, or user data.
  • Place breakpoints at natural stable/varying boundaries. After system, tools, docs.
  • Log cache stats on every response. Catch regressions fast.
  • Keep TTL in mind. 5min ephemeral won't help low-frequency workloads. Use 1hr beta.
SK
Tested by Sana K.
Researcher · AI Error Hub
Last verified: Nov 15, 2026 · SDK: anthropic 0.42

Frequently Asked Questions

Haiku's cost per token is much lower, so the overhead of managing a cache entry needs a larger prefix to be worthwhile. Anthropic sets the floor higher to prevent cache thrashing on tiny prompts.

Yes, put cache_control on the last tool. But total tools + preceding content must still meet 1024 tokens. Small tool definitions won't hit the floor alone.

No, cache is scoped per organization + API key + model. Different keys = different caches even for identical content.

Not guaranteed but very reliable within TTL. Cache eviction can happen under high memory pressure. In practice, hit rates are >99% for content sent within the 5-minute window.

Yes, cache_creation_input_tokens billed at 1.25× input rate ($3.75/M for Sonnet vs $3.00/M input). Break-even after 2 uses. 10 uses = 87% savings vs no cache.