Claude went down again on August 24 — elevated errors across claude.ai, the API, Claude Code, and Claude Cowork, starting around 05:06 UTC, resolved roughly three hours later. Unremarkable on its own, except it’s not an outlier: Anthropic has logged disruptions on August 5, 12, 13, 16, 18, 20, and now 24, and by most tracking services that puts the 2026 total north of 160 incidents — this despite a $71B compute buildout meant to fix exactly this. I’ve had three different clients this year ask me some version of “should we be worried our product depends on Claude/GPT/Gemini being up,” and my answer hasn’t changed: the model provider going down is not a hypothetical you plan around, it’s a recurring event you architect for, the same way you’d architect for a database failover or a third-party payment gateway timing out.

The mistake I see most often isn’t “no error handling” — it’s error handling that only covers the failure mode that’s easy to imagine (HTTP 500) and misses the ones that actually bite in production.

The four failure modes, not just one

Most teams build a try/catch around their LLM call, log the error, and call it done. That covers maybe a quarter of what actually goes wrong:

  1. Hard failure — 5xx, connection refused, timeout. Loud, easy to catch, easy to alert on.
  2. Rate limiting — 429s. As of early 2026, rate-limit errors accounted for roughly 60% of all LLM API errors industry-wide, which tells you this is the common case, not the edge case.
  3. Silent quality degradation — the request succeeds, returns a 200, and the output is subtly worse: truncated reasoning, a checkpoint swap upstream, context window pressure causing the model to wrap up early. This is the one a circuit breaker built only on HTTP status codes will never catch.
  4. Partial outage — one model tier is down (Opus) while another (Haiku) is fine, or one region is degraded while another isn’t. Binary up/down health checks miss this entirely.

If your resilience code only handles case 1, you’re covered for the outages that make headlines and exposed for the ones that quietly cost you the most support tickets.

The layered pattern that actually holds

The architecture that’s worked across the systems I’ve touched this year has four layers, applied in order:

type LLMResult = { text: string; provider: string; degraded: boolean };

async function callWithResilience(prompt: string): Promise<LLMResult> {
  // 1. Retry transient failures with backoff + jitter, honoring Retry-After
  for (const provider of [primaryProvider, secondaryProvider, tertiaryProvider]) {
    if (circuitBreaker.isOpen(provider.name)) continue; // 2. Circuit breaker

    try {
      const res = await retryWithBackoff(() => provider.call(prompt), {
        maxAttempts: 3,
        respectRetryAfter: true,
      });
      circuitBreaker.recordSuccess(provider.name);

      // 3. Quality gate, not just an HTTP status check
      if (looksTruncatedOrDegraded(res)) {
        circuitBreaker.recordDegradation(provider.name);
        continue; // fall through to next provider
      }
      return { text: res.text, provider: provider.name, degraded: false };
    } catch (err) {
      circuitBreaker.recordFailure(provider.name);
      // fall through to next provider
    }
  }

  // 4. Bulkhead: last resort is a cached/canned response, not a hard user-facing error
  return { text: cachedFallbackResponse(prompt), provider: "cache", degraded: true };
}

Four things worth calling out that aren’t obvious from the code shape alone:

The circuit breaker needs its own failure signal, separate from raw exceptions. recordDegradation exists on purpose — if you only trip the breaker on thrown errors, a provider that’s silently returning garbage stays in rotation indefinitely. I define “degraded” per use case: for a summarization endpoint it might be output length below a threshold; for a coding agent it might be a diff that fails to parse.

Provider order isn’t fixed — it’s a routing decision. The [primaryProvider, secondaryProvider, tertiaryProvider] list should be informed by more than “who’s up.” Cost and latency budget matter — I’ve seen teams put a cheaper model second in the chain not as a downgrade but as the correct fallback for a task that doesn’t need frontier reasoning anyway. That’s the same “model routing” instinct that’s becoming standard practice as pricing gaps between frontier and mid-tier models widen (a topic worth its own post).

The bulkhead is a UX decision, not just an engineering one. A cached fallback response with degraded: true set lets your UI show “answered from cache, may be less current” instead of either lying about freshness or throwing a 500 at the user. Decide what “acceptable degraded” looks like for your product before the outage, not during it — that’s a product conversation, and it’s much worse to have it live during an incident.

Test the failure path, not just the happy path. The single highest-leverage thing I’ve added to client codebases this year is a chaos test that forces the primary provider to fail in CI and asserts the fallback chain actually engages and returns something coherent. Most teams that “have a fallback” have never actually exercised it — the first real test is the incident itself, which is the worst possible time to discover the fallback provider’s API contract drifted six months ago.

What this actually costs you

None of this is free. A second provider integration means a second set of prompts to maintain (models don’t respond identically to the same prompt), a second billing relationship, and genuine latency overhead on the happy path if you’re not careful about how retries are structured. For a low-stakes feature, that cost isn’t worth paying — a plain error message and a retry button is a legitimate answer. The judgment call is scoping this to the parts of your product where an LLM call being down actually blocks a user from doing something they came to do, and building the layered pattern only there. Building it everywhere is as much of a mistake as building it nowhere.

166 outages and counting isn’t a reason to distrust any particular provider more than another — every frontier lab has had a rough year of infrastructure scaling pains. It’s a reason to stop treating “the model API is up” as an assumption your architecture is allowed to make.

Export for reading

Comments