DEV Community

Creeta
Creeta

Posted on • Originally published at news.creeta.com

Claude's prompt cache fails silently below 1,024 tokens

Prompt caching on the Claude API is the difference between an agent loop that costs $5 and one that costs $50 — but only if the cache actually forms. The most common reason it doesn't is a floor that produces no error message at all.

What's New: Automatic Caching, Diagnostics — and the Floor Nobody Warns You About

Claude's prompt cache has a minimum cacheable prefix, and it is model-dependent rather than a single global number: 512 tokens for Opus 5, Fable 5 and Mythos 5; 1,024 for Opus 4.8, Sonnet 5, Sonnet 4.6/4.5 and Opus 4.1/4; 2,048 for Opus 4.7; and 4,096 for Opus 4.6, Opus 4.5 and Haiku 4.5 . Below that floor nothing is cached and no exception is raised — the request bills at the full input rate and the only signal is both cache token counters sitting at zero .

Two 2026 platform changes make this easier to catch. On February 19, 2026, automatic caching went GA on the Messages API: a single top-level cache_control field places the breakpoint on the last cacheable block and advances it as the conversation grows . On May 13, 2026, cache diagnostics entered public beta — with the header cache-diagnosis-2026-04-07, a request returns a cache_miss_reason instead of silence . Neither change removes the token floor; they just make it visible.

Before You Rely on This in Production: What Your Setup Needs

Caching requires three things in place before the numbers move: Messages API access with cache_control block support, a model whose minimum cacheable prefix your stable content actually clears, and a habit of reading usage metadata instead of trusting the discount. The 1-hour TTL has been generally available without a beta header since August 13, 2025, but mid-conversation tool changes still need the mid-conversation-tool-changes-2026-07-01 header .

The floor is model-dependent and varies by a factor of eight. A prompt below it can never cache, no matter where you put the marker .

Minimum cacheable prefix Models
512 tokens Opus 5, Fable 5, Mythos 5
1,024 tokens Opus 4.8, Sonnet 5, Sonnet 4.6 / 4.5, Opus 4.1 / 4
2,048 tokens Opus 4.7
4,096 tokens Opus 4.6, Opus 4.5, Haiku 4.5

Ground truth lives in usage: cache_creation_input_tokens (split into ephemeral_5m_input_tokens and ephemeral_1h_input_tokens) and cache_read_input_tokens. Budget your markers too — a request carries at most four explicit cache_control breakpoints, and automatic caching consumes one of those slots, so combining it with four manual markers returns a 400 .

How to Confirm Your Cache Actually Formed

Confirming a cache formed takes one measurement and one repeat: count the tokens in the exact content sitting before your cache_control breakpoint, compare that count against your model's minimum, then send the identical request twice and read usage.cache_creation_input_tokens on call one and usage.cache_read_input_tokens on call two. Anything else — a lower input_tokens figure, a faster response — is circumstantial. Below the model floor nothing is cached and no error is raised .

Placement decides what gets counted. Put the marker on the last stable block: the final entry in the tools array to cache the whole tool prefix, or the end of a static system prompt when you have no tools. For an mcp_toolset, mark the toolset entry so the breakpoint lands on the last expanded tool .

The following snippet is illustrative — it was not executed, since it needs a live ANTHROPIC_API_KEY. It deliberately marks a sub-1,024-token block so you can watch both counters come back empty:

import json
import os
import sys
import urllib.request

key = os.environ.get("ANTHROPIC_API_KEY")
if not key:
    sys.exit("needs ANTHROPIC_API_KEY")

body = {
    "model": "claude-3-5-sonnet-20241022",
    "max_tokens": 1,
    "messages": [
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": "This cacheable block is intentionally below 1,024 tokens.",
                    "cache_control": {"type": "ephemeral"},
                },
                {"type": "text", "text": "\nReply with OK."},
            ],
        }
    ],
}

req = urllib.request.Request(
    "https://api.anthropic.com/v1/messages",
    data=json.dumps(body).encode(),
    headers={
        "content-type": "application/json",
        "anthropic-version": "2023-06-01",
        "x-api-key": key,
    },
)

with urllib.request.urlopen(req, timeout=30) as r:
    data = json.load(r)

usage = data["usage"]
print("cache_creation_input_tokens =", usage.get("cache_creation_input_tokens"))
print("cache_read_input_tokens =", usage.get("cache_read_input_tokens"))
print("No error is raised; sub-1024-token prompt cache is just not used.")
Enter fullscreen mode Exit fullscreen mode

If both counters read zero on the second call, the prefix is under the floor — pad it with more static content or move more stable material ahead of the marker. If they read zero only sometimes, stop guessing and opt into the diagnostics beta that entered public preview on May 13, 2026: send header cache-diagnosis-2026-04-07 with diagnostics.previous_message_id and the response returns a cache_miss_reason naming the first point where the prefix diverged from the prior turn .

Then keep watching it. Anthropic's Claude Code team treats hit rate as a reliability signal, not an accounting line:

"We run alerts on our prompt cache hit rate and declare SEVs if they're too low… monitor your cache hit rate like you monitor uptime." — Claude Code team, Anthropic (source: Lessons from building Claude Code)

Log creation and read counters separately, broken out by model, TTL and workflow. A single aggregate number hides the case that matters most: a prefix that writes on every call and never reads.

Gotchas That Show Up After the Cache Forms

A formed cache is not a stable cache. Once the prefix hits, the next class of failures comes from configuration flags and server-side behavior that quietly rewrite or invalidate what you cached. The most common surprise: when a request already carries a cache_control marker and Claude calls a server tool — web search, web fetch, or code execution — the API automatically inserts a breakpoint on the server tool result before the next iteration, and that breakpoint always uses the 5-minute TTL regardless of your markers . A 1-hour-only configuration will therefore still report ephemeral_5m_input_tokens writes. That is expected behavior, not a leak.

The invalidation triggers worth logging explicitly:

  • Changing tool_choice or disable_parallel_tool_use invalidates the messages cache .
  • Toggling web search or citations invalidates both system and messages .
  • Changing thinking parameters or output_config.effort invalidates messages on every model — and tools plus system on models that render that config ahead of them .

Mixed TTLs are permitted, but ordering is strict: 1-hour blocks must appear before 5-minute blocks. Billing is then computed across three positions — the highest cache hit (A), the last 1-hour breakpoint after it (B), and the last breakpoint (C) — charging reads for A, 1h writes for B−A, and 5m writes for C−B . Invert that order and the math stops describing what you actually pay.

What to Try Next: Diagnostics, Keepalive, and the Break-Even Math

Point the diagnostics beta at your worst-performing call path first. With the header cache-diagnosis-2026-04-07, a request can pass diagnostics.previous_message_id and get back a cache_miss_reason naming the first point where the prefix diverged from the prior turn — a direct answer instead of bisecting a 30 KB prefix by hand.

Then check the arithmetic before you widen the TTL. A 5-minute write costs 1.25× base input and a read 0.1×, so two calls on a warm prefix run 1.35× versus 2× uncached — payback on the second read. A 1-hour write at 2.0× needs roughly two to three reads to clear . If you plan to hold a prefix warm with keepalive pings, Khailo's July 24, 2026 paper derives break-even horizons of about 46 minutes for the 5-minute tier and 3.3 hours for the 1-hour tier at measured provider parameters .

One last default worth knowing: Claude Code's client requests the 1-hour TTL automatically on a subscription, but falls back to 5 minutes on an API key unless you set ENABLE_PROMPT_CACHING_1H=1 . The takeaway is narrow and testable: log cache_creation_input_tokens and cache_read_input_tokens per model and workflow, and treat a flat-zero pair as a defect report — not a silent cost you keep paying.

Frequently asked questions

What's the smallest prefix Claude will actually cache?

It depends on the model, and the spread is wide: 512 tokens on Opus 5, Fable 5 and Mythos 5; 1,024 tokens on Opus 4.8, Sonnet 5, Sonnet 4.6, Sonnet 4.5, Opus 4.1 and Opus 4; 2,048 tokens on Opus 4.7; and 4,096 tokens on Opus 4.6, Opus 4.5 and Haiku 4.5 . The floor is not monotonic across versions — Opus 4.8 dropped to 1,024, below Opus 4.7's 2,048 . Check the number for the exact model you deploy before assuming a compact system prompt will cache at all.

How do I know whether my prompt cache actually formed?

Read two fields from the API response: usage.cache_creation_input_tokens and usage.cache_read_input_tokens. A non-zero creation count means a prefix was written; a non-zero read count means it was reused. Both at zero means nothing was cached, no matter how large input_tokens looks — total input is read + creation + input, so a fat input_tokens value with flat-zero cache counters is exactly the failure signature . The cache_creation object splits writes into ephemeral_5m_input_tokens and ephemeral_1h_input_tokens if you need TTL-level attribution.

Does the API throw an error if my prefix is too short to cache?

No. A request with a cache_control marker on a below-threshold prefix succeeds normally and bills at the full input rate — no warning, no exception, no header. The only observable signal is both cache token counters staying at zero . That is why the Claude Code team treats hit rate as an operational metric rather than an optimization detail: they run alerts on prompt cache hit rate and declare SEVs when it drops .

Should I use the 5-minute or 1-hour TTL?

Use the 5-minute tier for active chat loops, short tool-use bursts and retries: at 1.25× base input for the write and 0.1× for the read, it clears break-even on the second use versus two uncached calls . Use the 1-hour tier — 2.0× write — only when expensive context setup will be re-read across a long job or many worker calls, since it needs roughly two to three reads to pay back . If you hold a prefix warm with keepalive pings, the measured break-even horizons are about 46 minutes for the 5-minute tier and 3.3 hours for the 1-hour tier . Mixed TTLs are allowed, but 1-hour blocks must sit before 5-minute blocks.

Why does a 1-hour-only cache config still show 5-minute writes?

Because server tools insert their own breakpoint. When a request already carries a cache_control marker and Claude calls a server tool — web search, web fetch or code execution — the API automatically adds a breakpoint on the server tool result before the next iteration, and that breakpoint always uses the 5-minute TTL regardless of the markers you set . So a configuration you believe is 1-hour-only will still report ephemeral_5m_input_tokens writes. This is documented behavior, not a defect — budget for it rather than debugging it.

Can I add or remove tools mid-conversation without losing the cache?

Yes, on recent models with an opt-in header. Since 24 July 2026, the mid-conversation-tool-changes-2026-07-01 header lets you add or remove tools between turns while preserving the cache on Fable 5, Mythos 5, Opus 4.8 and Opus 5; a related change on 28 May 2026 permits mid-conversation role: "system" messages so instructions can change without invalidating the prefix . For older models, the structural fix is defer_loading with the tool-search tool: deferred definitions never enter the prefix, and discovered tools append as tool_reference blocks in the conversation instead .

How much cost reduction is realistic?

Independent measurement lands below vendor headline figures. Anthropic's original announcement cited up to 90% cost and 85% latency reduction for long repeated prompts , while an evaluation of 500+ agent sessions on DeepResearch Bench with 10,000-token system prompts across three vendors reported 41–80% cost reduction and 13–31% time-to-first-token improvement . That paper also found naive full-context caching can increase latency; the most consistent strategy was caching stable system context while keeping volatile tool results out of the reusable prefix.

Watch / Sources

Last updated: 2026-08-06. Model minimum-token thresholds, TTL pricing multipliers and beta header names were checked against Anthropic's prompt-caching documentation and API release notes on this date; verify per model and per deployment target before relying on them in production.

Top comments (0)