Claude Messages API Message Threads: Separate 5-Minute Server State, Prompt Caching, and 404 Replays
Learn how Claude Messages API Message Threads (message-threads-2026-08-12) cut multi-turn TTFT, why prompt caching is still required for token savings, and how to handle 5-minute expiry replays.
A conceptual illustration showing only the new turn delta traveling from the client's full conversation history into an ephemeral 5-minute server memory ring and four prompt-cache slots before inference. This is not an actual console screenshot.
On November 2, 2026, Anthropic launched Message Threads (message-threads-2026-08-12 beta) for the Messages API, allowing long-running conversations and agent loops to send only new messages on subsequent turns rather than re-uploading the entire conversation history every time. You initialize a thread with thread: {"type": "create"} on the first request, and follow up with thread: {"type": "continue", "previous_message_id": "..."} carrying only the new turn.
The core engineering takeaway: Message Threads optimize request transport and preprocessing latency (TTFT), but they do not replace Prompt Caching (cache_control) or client-side conversation storage. Because the model still reads the reconstructed conversation on every turn, your input token bill remains identical unless you also configure prompt caching. Moreover, server-side threads are best-effort ephemeral state that expire after 5 minutes of inactivity and are deleted within 1 hour of last use—meaning your application remains the source of truth and must be ready to replay the full conversation in a new create request whenever a continue call returns 404 thread_not_found.
This guide analyzes the request field contracts, the 4-breakpoint caching limit, and replay recovery patterns based on the Claude Platform Release Notes, the official Message Threads documentation, and the Prompt Caching guide. It does not report live production TTFT measurements; the local Python checks below verify request validation and replay sanitization logic.
1. Separate Message Threads, Prompt Caching, and Managed Agents Sessions
Although all three features deal with multi-turn context on the Claude platform, they target different layers of the stack and carry distinct billing and lifecycle rules.
| Dimension | Message Threads ( message-threads-2026-08-12) | Prompt Caching ( cache_control) | Managed Agents Session Threads |
| Primary Bottleneck Solved | Eliminates repeated payload uploads, JSON validation, and re-tokenization of prior turns (lowers and stabilizes TTFT) | Reuses KV cache prefixes across turns to cut model compute time and input token cost (0.1x base input rate; 0.025x on Fable 5.1 & Mythos 5.1) | Manages full agent execution state including cloud sandboxes, tool loops, and mounted memory stores |
| Effect on Token Billing | No token discount. You upload fewer bytes, but standard input/output token rates still apply to the full reconstructed conversation | Direct cost reduction. 5-minute TTL write is 1.25x, 1-hour TTL write is 2x, and cache reads drop to 10% (or 2.5–5% on select models) of base input price | Bills per underlying model request and sandbox usage, with optional hard session spend caps |
| Lifecycle & Storage | Ephemeral server memory only. Expires 5 minutes after the last request and is deleted within 1 hour (no list, read, or delete endpoints) | 5-minute default (or 1-hour) TTL measured from request start and refreshed on cache hits | Durable server-side session resource inspectable via GET /v1/sessions/... and event streams |
| Source of Truth | Your client application. The API does not confirm on create or continue whether a turn was stored; you discover expiry on the next continue | The request payload itself; a cache miss still succeeds because the full prompt is present | Anthropic's Managed Agents service persists the session event log |
In short, for 500K–1M token agent loops, enable Message Threads to shrink network transfer and validation overhead, and pair them with a top-level cache_control: {"type": "ephemeral"} (or explicit block breakpoints) so each turn also reads prior history from the prompt cache.
2. Create-Only, Fixed, and Free Fields: Avoiding thread_fingerprint_mismatch
When building raw HTTP requests or calling client.beta.messages.create directly without the SDK thread helper, most 400 errors stem from violating the field contract between create and continue requests.
| Field Group | Parameters | On create Requests (Turn 1 / Replay) | On continue Requests (Later Turns) | Failure Mode & Operational Rule |
| Create-only | system, tools | Send full definitions along with any block-level cache_control breakpoints | Must be omitted completely. Sending either field—even set to null—is rejected | Returns 400 invalid_request_error immediately if system or tools appears in a continue payload |
| Fixed | model, tool_choice, thinking, mcp_servers (except auth token), context_management, output_config, skills in container, top-level cache_control, anthropic-beta set, presence of fallbacks | Send initial configuration values | Must be sent on every turn with identical values. The API does not persist MCP server URLs or container IDs across turns | Any divergence returns 400 thread_fingerprint_mismatch naming the mismatched fields. Start a new create thread to change a fixed field |
| Free | max_tokens, stop_sequences, stream, metadata, service_tier | Send desired values | May change freely from turn to turn | If max_tokens truncates a response mid-tool-call, the API serves the reply but does not store the turn, causing a 404 on the next continue |
Two subtle interactions require careful handling in production code:
- The 4-breakpoint
cache_controlceiling across turns and replays: Any request usingthreadmay carry at most 4cache_controlbreakpoints. The top-levelcache_controlcounts as 1, and breakpoints placed onsystemandtoolsduring thecreaterequest continue to count on every subsequentcontinuerequest. Breakpoints on earlier stored message blocks do not count duringcontinuecalls—but if the thread expires and you replay the full conversation in a newcreaterequest without stripping those older message breakpoints, exceeding 4 total breakpoints triggers400 thread_unsupported_request(unsupported: ["cache_control"]).
- Preserved thinking blocks when changing
systemortools: On Claude Opus 5.5, Sonnet 5.5, Haiku 5.5, Fable 5.1, and Mythos 5.1, starting a newcreaterequest with modifiedsystemortoolsinvalidates earlierthinkingandredacted_thinkingblocks in the history and can return a400error. Strip all earlier thinking blocks from the replayed history whenever you changesystemortools.
3. Recovery Matrix for Expiry, Refusals, and Fallback Models
Because a thread is append-only and each response ID can be continued at most once, production agents need deterministic recovery logic for five common outcomes.
| Condition / Error Code | Root Cause | Required Client Recovery Action |
404 thread_not_found | 5-minute idle TTL expired, 1-hour max lifetime reached, prior turn ended in a truncated tool call, different API key/workspace/IAM principal used, or first image added to a text-only thread | Strip cache_control breakpoints from earlier message blocks in your local history copy and send the full conversation with system and tools in a new thread: {"type": "create"} request. If the very next continue still returns 404, fall back to unthreaded requests |
400 thread_already_continued / thread_continue_in_flight | A retry reused a previous_message_id that was already continued, or the prior request is still generating after a dropped stream | Wait for the in-flight turn or replay the full conversation in a new create request. When streaming, never send a continue request after message_start—wait until message_stop arrives |
stop_reason: "refusal" | Safety classifiers declined the turn. The server may still hold the refused turn in the thread, and a continue call cannot remove it | Never continue from a refused response ID. Drop or rephrase the offending turn in your local copy and start a new create request from the history prior to the refusal |
fallback_message in usage.iterations | Server-side fallback ( server-side-fallback-2026-07-01) routed the turn to a fallback model. Because a thread binds to a single model, fallback turns are never stored | Do not wait for a 404 on the next turn. Immediately send the full conversation (including the fallback response) in a new create request, either retrying the primary model or pinning the new thread to the fallback model |
400 thread_unsupported_request | Request combines thread with Message Batches, Token Counting, on-demand Compaction (compact-2026-09-04), >4 cache breakpoints, HIPAA-enabled orgs, or Inference Hooks | Inspect error.details.unsupported (e.g., cache_control, context_management, hipaa, inference_hooks) and either remove the conflicting feature or send the request without thread |
4. Validating Thread Requests and Sanitizing Replays in Python
If you use the official SDK helper (client.beta.messages.thread(...)), it automatically omits system and tools on continue turns, replays once on thread_not_found, and strips earlier message breakpoints on replay. When orchestrating raw payloads across custom proxies or multi-provider routers, the dependency-free Python validator below enforces these invariants before a request hits the network.
Scope Verified Locally
Executing this validation and replay-sanitization logic in a local Python environment verified four boundary scenarios:
- Valid
continueturn at the 4-breakpoint boundary: Combining a top-levelcache_control(1), initialsystembreakpoint (1), initialtoolsbreakpoint (1), and one new message block breakpoint (1) while raising the freemax_tokensfield from 1024 to 2048 passed with zero errors.
- Create-only field leak detection: Including
"system": Noneon acontinuepayload was flagged as400_create_only_present:system.
- Fixed-field drift detection: Switching
modelfromclaude-opus-5-5toclaude-sonnet-5-5mid-thread was caught as400_thread_fingerprint_mismatch:model.
- Replay breakpoint sanitization: Replaying a 3-turn history where every user turn carried a block breakpoint initially totaled 6 breakpoints (exceeding the limit of 4), and
strip_earlier_message_breakpoints()reduced the replay payload to exactly 4 breakpoints.
5. Production Rollout Checklist (Plus November 30 Deprecations)
- Combine
threadwith top-levelcache_control: Passcache_control: {"type": "ephemeral"}on every request in the thread so you get both smaller network uploads and cached input token pricing.
- Configure an unthreaded fallback path: If the first
continueright after a replay still returns404 thread_not_found, drop thethreadparameter and continue statelessly (or setunsupported_request_behavior="continue_unthreaded"in the SDK helper).
- Keep Token Counting and Compaction separate: Never pass
threadto/v1/messages/count_tokensor alongside on-demand compaction (compact-2026-09-04). When compacting history, start a freshcreatethread seeded with the summary message.
- Prepare for the November 30, 2026 removals: Per the October 30 and September 30 release notes, both fast mode (
speed: "fast") onclaude-opus-4-8and theclaude-sonnet-4-5-20250929model will be removed on November 30, 2026 and will return hard errors rather than falling back. Migrate long-running agent loops toclaude-opus-5-5orclaude-sonnet-5-5ahead of the deadline.
References
Last verified: November 4, 2026
Korean version: Claude Messages API Message Threads 해부: 5분 서버 스레드·프롬프트 캐싱·404 리플레이 분리해 설계하기