◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/API & SDK
⬡ API & SDK/2026-06-15Advanced

When a Model Disappears Without Warning: A State Machine for Retirement, Withdrawal, and Overload

A model can become unusable in hours for reasons that have nothing to do with a technical outage. This guide models three distinct flavors of 'unavailable'—retirement, withdrawal, and transient overload—as one availability state machine, with a router that keeps automated pipelines running. Working TypeScript and Python included.

claude-api83architecture10resilience10model-migration5production111fallback9

✦ Premium Article

One morning, before kicking off the automated publishing across my four sites, I opened the changelog and the official announcements as usual. One of the models I had been using for smoke tests the day before had been suspended within hours for reasons that were not a technical outage—withdrawn for all foreign-national users. There was no retirement email. The API had not started returning 429. The model name that answered yesterday simply stopped being accepted today.

What I run is a content-generation pipeline. No lives or payments depend on it. Even so, the fact that a headless job running overnight assumed a specific model name suddenly felt heavy. I did have a fallback, but that branch only anticipated 429 and 529—transient overload. Permanent retirement and a sudden, externally driven withdrawal were lumped together inside the same fallback().

What I learned that day is that "unavailable" wears several different faces, and pouring them all into one exception handler leads you to make the wrong recovery decision. Transient overload returns in minutes. Hammering a withdrawn model every few minutes only piles up wasted failures. A retired model never comes back no matter how long you wait. This article designs an availability state machine that holds these three as distinct states, and a router built on top of it that changes behavior per state.

"Unavailable" Has Three Faces

Let me classify, by nature, the kinds of "unavailable" that stop automation. I settled on three.

The first is retirement. The vendor announces it in advance, and past a certain date the model is permanently no longer accepted. Today the older claude-sonnet-4 and claude-opus-4 retired from the API. This is predictable and the successor is known. Waiting won't help, so the right move is to switch to the successor the moment you detect it.

The second is withdrawal. Not a technical outage, but a short-notice suspension driven by policy, legal, or security decisions—external factors. There is no announced date, and recovery carries the uncertainty of "maybe it will come back eventually." The case I opened with is this. Unlike retirement, no successor is waiting, so you need to decide whether to shift sideways to another logical role or temporarily scale that job down.

The third is overload. Transient unavailability that recovers on its own within minutes to hours, like 429 Too Many Requests or 529 Overloaded. If you perform a permanent switch here, you stay needlessly downgraded from the higher model you actually wanted. The correct response is exponential backoff, then return to the original once it recovers.

Handle all three in a single catch, and you get misjudgments: applying overload-style backoff endlessly to a withdrawn model, or permanently downgrading an overloaded model as if it were retired. That is why the states are held separately.

Decouple Logical Roles From Physical Model IDs

Before designing the state machine, I set up the registry that everything rests on. As long as business code knows model strings directly, every withdrawal or retirement means rewriting dozens of call sites. As an indie developer I learned this the hard way at first.

So the only thing the code references is a logical role (fast / balanced / deep), and the mapping from role to physical model ID, along with each model's availability state, is consolidated in one place.

// model-registry.ts
export type Role = "fast" | "balanced" | "deep";
export type Availability = "available" | "overloaded" | "withdrawn" | "retired";
 
interface ModelEntry {
  id: string;            // physical model ID
  inputPer1M: number;    // input token price (USD / 1M tokens)
  outputPer1M: number;   // output token price
  availability: Availability;
  understudy?: string;   // in-role fallback ID for withdrawal/retirement
}
 
// Each role holds a priority-ordered candidate list; the head is first choice.
export const REGISTRY: Record<Role, ModelEntry[]> = {
  deep: [
    { id: "claude-opus-4-8", inputPer1M: 5.0, outputPer1M: 25.0, availability: "available" },
    { id: "claude-sonnet-4-6", inputPer1M: 3.0, outputPer1M: 15.0, availability: "available" },
  ],
  balanced: [
    { id: "claude-sonnet-4-6", inputPer1M: 3.0, outputPer1M: 15.0, availability: "available" },
    { id: "claude-haiku-4-5", inputPer1M: 1.0, outputPer1M: 5.0, availability: "available" },
  ],
  fast: [
    { id: "claude-haiku-4-5", inputPer1M: 1.0, outputPer1M: 5.0, availability: "available" },
  ],
};

Business code requests a model by logical role, like route("deep"), and never knows the physical ID. To remove a withdrawn model from every job, you rewrite the availability of one entry in REGISTRY. That single-file containment is the core of the design.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦A transition table that treats 'retirement' (announced permanent removal), 'withdrawal' (short-notice suspension), and 'overload' (minutes-to-hours of 429/529) as separate states, plus a router whose behavior changes per state.
✦A ModelRegistry that maps logical roles (fast / balanced / deep) to physical model IDs in one file, so a withdrawn model can be removed from every job with a one-line state change (TypeScript and Python).
✦A daily preflight probe that detects withdrawal with a single tiny request, and a cost-accounting routine that re-prices tokens on fallback so your monthly totals never quietly drift.
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⬡ API & SDK2026-04-23
High-Availability Patterns for the Claude API — Making Sonnet/Haiku/Opus Fallback Work in Production
A single-model Claude API integration will fall over the first time rate limits or a regional hiccup land at peak hours. This is the production pattern for a Sonnet → Opus → Haiku fallback chain, with circuit breakers, streaming coverage, and the pitfalls you only learn the hard way.
⬡ API & SDK2026-03-27
Claude API Production Resilience Patterns — Model Routing, Circuit Breakers, and Fallback Strategies for Indie Teams
Production resilience patterns for Claude API: circuit breakers, intelligent model routing, fallback chains, exponential backoff with jitter, and disaster recovery — with TypeScript implementations and operational notes from an indie developer.
⬡ API & SDK2026-05-26
Stabilizing Claude API Structured Responses in Production — Notes on tool_use, JSON Schema, and Layered Validation
Getting Claude to return JSON takes a few lines. Keeping that JSON usable in production is a different problem. Here is the layered design I landed on after running a wallpaper classification pipeline through Claude API, built around tool_use, JSON Schema, and domain validation.