Claude Code 2.1.251 model-switch hooks: stop cache rebuilds from becoming surprise spend
Quick answer
Claude Code v2.1.251, published on August 28, 2026, adds PreModelSwitch and PostModelSwitch hooks plus prompt-cache visibility in /usage and the status line. Together, they let you turn a model change from an invisible session event into a policy decision with an audit trail.
Use PreModelSwitch to allow, deny, or ask before a user- or client-requested switch. Use PostModelSwitch to observe every durable session-model change, including automatic fallback and resume. Before enforcing anything, capture context_tokens, prompt_cache_warm, estimated_cache_write_usd, and pricing. The estimate is a planning signal, not an invoice: it excludes the next response, and the server may not rebuild the entire cache.
This is a direct AI coding-workflow change. It does not make model routing cheap, deterministic, or fully interceptable by itself.
Who this is for
This guide is for independent developers and small teams that use long Claude Code sessions, switch between Sonnet and Opus, enable fast mode, resume older sessions, or let an SDK or Remote Control client select models. It is especially useful when a warm prompt cache is valuable enough that a mid-task switch should be deliberate.
If you only run short one-shot prompts, a lighter policy may be enough: record the switch and keep working. If you operate long agent sessions or a shared gateway, treat model changes as a cost and governance boundary. Keep this separate from the filesystem authority covered by the Claude Code restricted-mode checklist.
What changed in 2.1.251
The official 2.1.251 release and the current hooks reference establish three useful surfaces:
| Surface | What it can see | What it can do |
|---|---|---|
PreModelSwitch |
Requested source and target models, context size, warm-cache state, TTL, estimated cache-write cost, and pricing source |
allow, deny, or ask before the switch |
PostModelSwitch |
The completed switch, including automatic fallback and resume | Log it or add model-specific context; it cannot block |
SessionStart on resume/fork |
Session age, context tokens, likely cache expiry, and estimated cache-write cost | Warn or annotate before the first resumed request |
The same release adds a per-session prompt-cache line to /usage and a prompt_cache object for status-line scripts. Behind a Claude apps gateway with spend limits, rate_limits.spend_limit exposes the applicable usage percentage and reset time. These are observability fields, not a universal billing API.
Know which transitions are actually covered
The first rollout mistake is assuming one pre-hook sees every model change. It does not.
| Transition | PreModelSwitch |
PostModelSwitch |
Operator response |
|---|---|---|---|
/model, picker, or /config request |
Yes | Yes | Apply allowlist and cache-cost policy |
SDK or Remote Control set_model
|
Yes | Yes | Avoid ask unless refusal is acceptable |
| Fast mode that changes the model | Yes | Yes | Treat it as a new model cache |
| Automatic model fallback | No | Yes | Detect and reconcile after the change |
| Restored model on session resume | No | Yes | Pair with SessionStart cost fields |
| One-turn fallback-model-chain substitution | No | No | The durable session model did not change |
Only interactive /model can display an ask prompt. On non-interactive -p, /config, SDK, and Remote Control surfaces, ask behaves as a refusal. Also check to_model inside the hook: a custom gateway model may not resolve to the canonical name used by the matcher.
A six-stage rollout
1. Pin the version and keep a rollback
Deploy 2.1.251 to a disposable project first. Record the actual binary path and claude --version; do not assume an editor extension, desktop app, and terminal are using the same build. Keep the previous version available until the canaries below pass.
2. Observe before blocking
Run a representative long task without changing models. Capture the new /usage prompt-cache line and, if you use a custom status line, these fields:
prompt_cache.warm
prompt_cache.hit_ratio
prompt_cache.misses
prompt_cache.expected_rebuilds
prompt_cache.ttl
The prompt-caching documentation explains why model and effort are both cache keys. Changing either mid-session causes the next request to read the whole conversation without cache hits.
3. Start with three policy outcomes
Use a small decision table instead of a hard-coded โOpus is expensiveโ rule. The same principle applies when reconciling inherited access in the Copilot model-governance checklist: availability, selection, and cost are different facts.
-
denywhen the target model is outside the approved model set. -
askwhen the current cache is warm and the estimated cache write exceeds your session threshold. -
allowwhen the cache is already cold, the context is small, or the switch is part of an approved task phase.
Log from_model, to_model, source, context_tokens, prompt_cache_warm, cache_ttl, estimated_cache_write_usd, and pricing. Never log credentials or transcript content.
4. Reconcile after every durable switch
Use PostModelSwitch for an append-only event. Include whether the source was command, picker, sdk, auto, or resume. An automatic fallback bypasses the pre-hook, so post-switch evidence is what tells you the session moved.
If you add model-specific instructions through additionalContext, keep them short. They arrive with the next request, and rapid repeated switches deliver only the last target model's output.
5. Put stale resumes behind a warning
For resumed or forked sessions, SessionStart now provides seconds_since_last_response, context_tokens, prompt_cache_likely_expired, and estimated_cache_write_usd. Warn when a long conversation is stale enough that resuming it is likely to rebuild the cache. The useful choice may be to resume, compact at a task boundary, or start a clean session; the estimate alone should not choose for you.
6. Compare policy to real outcomes
After a week, compare switch events with cache misses, latency, accepted-task quality, and provider or gateway usage. A policy that minimizes cache writes but forces the wrong model is not a win. Keep the model decision tied to the user task, then optimize where the evidence shows avoidable churn. For delegated work, reconcile model choice alongside the subagent concurrency budget instead of treating model and worker count as separate cost systems.
Eight acceptance canaries
| Canary | Expected evidence |
|---|---|
Warm-cache /model switch under threshold |
Pre-hook allows; post-hook logs one durable switch |
| Warm-cache switch over threshold | Interactive session asks with context/cost reason |
| Disallowed target model | Switch is denied and current model remains active |
SDK switch with an ask result |
Client receives refusal rather than waiting for a dialog |
| Automatic fallback | No pre-hook event; one post-hook event with source: auto
|
| Resume a stale long session |
SessionStart reports likely expiry and estimated cache write |
| Change effort without changing model | Cache rebuild is visible even though no model switch hook fires |
| Custom gateway model ID | Hook checks to_model; matcher ambiguity cannot bypass policy |
Store one redacted acceptance record per surface:
claude_model_policy:
version: 2.1.251
requested_source: command
from_model: claude-sonnet-5
to_model: claude-opus-5
cache_warm: true
context_tokens: 182340
estimated_cache_write_usd: 1.1396
pricing: catalog
decision: ask
post_switch_seen: true
outcome: accepted
The numbers above mirror the official documentation example; they are not a price quote for your account.
Building something? Take a 60-sec game break. Score to rank your product or profile on tapto.top and get more exposureโfree, no signup.
Common mistakes
- Treating
estimated_cache_write_usdas the final charge. It excludes the response and may overstate the cache rebuilt by the server. - Expecting
PreModelSwitchto block automatic fallback or resume. Those paths reachPostModelSwitchonly. - Returning
askfrom a headless or SDK workflow and assuming someone will see a dialog. - Using the matcher as the only guard for custom gateway IDs instead of checking
to_model. - Reading
prompt_cacheas fleet-wide or subagent-inclusive. It summarizes the main conversation for that session. - Changing model and effort together, then attributing the cache miss to only one of them.
FAQ
Do I need 2.1.251 for these hooks?
Yes. The official hooks reference marks PreModelSwitch, PostModelSwitch, the resume cache-cost fields, and the prompt-cache status object as requiring Claude Code v2.1.251 or later.
Can the pre-hook block automatic fallback?
No. Automatic fallback is not a requested switch, so it skips PreModelSwitch. Observe it with PostModelSwitch and decide whether the new model is acceptable for the next turn.
Does a model switch always rebuild the whole cache?
Each model has its own cache, so the next request to a different model has no hits from the current model's cache. The reported dollar figure remains an estimate because the serving layer may already hold a compatible prefix.
Should I always deny switches with a warm cache?
No. The stronger model may save more engineering time or improve the acceptance rate. Use the hook to expose the tradeoff, not to replace task-level judgment with a single cost threshold.
Sources
- Claude Code v2.1.251 release
- Claude Code hooks reference
- How Claude Code uses prompt caching
- Claude Code status line reference
- Tagged release commit
f1af9b1
Originally published at IndieSeek.
Top comments (0)