DEV Community

Cover image for OpenAI服务器故障:ChatGPT与Codex宕机应对指南
Dave Kurian
Dave Kurian

Posted on • Originally published at otf-kit.dev

OpenAI服务器故障:ChatGPT与Codex宕机应对指南

When your AI provider goes dark, your UI shouldn't

OpenAI's servers went down. ChatGPT stopped answering. Codex stopped completing. That's what the Sina report tells us — and the rest, for now, is waiting on OpenAI's own incident write-up.

But the interesting question isn't what broke at OpenAI. The interesting question is what your app does next, because the answer to that is something you control.

Frontier models are impressive engineering. OpenAI runs inference at a scale no single team can replicate. That scale is exactly why an outage at the wrong moment knocks out a chunk of the internet. The same property that makes a model worth using — its centralisation — is what makes it a single point of failure.

For the engineer shipping a product on top of that model, the answer isn't to avoid the model. The answer is to design the layer underneath so the app keeps shipping when the model blinks.

What the public record says, and what it doesn't

The Sina headline is sparse: server failure, ChatGPT, Codex, both down. No duration, no user count, no root cause yet. That's the state of the public record as of writing.

Most outages of this shape follow the same arc:

Phase What you see What you should do
t=0 Errors start in your logs Alert on the spike, not the noise
t+5min Status page updates — or doesn't Subscribe to status, don't poll
t+30min Some users still affected Communicate honestly, don't fake an ETA
t+resume Back to normal Write the post-mortem, ship the fix

The right time to build the response is before t=0. Not after, when the runbook is open on one monitor and Twitter is open on the other.

Principle one: never block the user on a single remote call

The classic mistake is wrapping the model in a synchronous await from a UI handler. When the provider times out, the user's request dies. Worse: they can't tell whether the model is slow, the network is gone, or the provider is down.

// bad — blocks the UI, no timeout, no fallback
async function onSubmit(prompt: string) {
  const reply = await openai.chat(prompt) // hangs forever on outage
  setReply(reply)
}
Enter fullscreen mode Exit fullscreen mode

The fix is three lines. Race the model call against a timeout, fall back to something safe, and queue the real answer for when the provider is back.

// good — bounded wait, fall back to a stub, never lie
async function onSubmit(prompt: string) {
  const reply = await Promise.race([
    openai.chat(prompt),
    timeout(8_000).then(() => null), // 8s is plenty for chat
  ])
  if (reply === null) {
    setReply(fallbackReply(prompt))   // cached, templated, or "we're back soon"
    enqueueRetry(prompt)              // replay later
    return
  }
  setReply(reply)
}
Enter fullscreen mode Exit fullscreen mode

Promise.race against a timeout is the smallest change that converts a silent hang into a visible failure mode. Pair it with a fallback and a retry queue and you've removed the worst of the outage UX.

Principle two: the UI is the part that doesn't churn

Models churn. Providers churn. Prices churn. The part that doesn't churn is the screen the user is looking at.

If your buttons, cards, dialogs, and forms live in a component layer that's independent of the provider SDK, an outage becomes a degraded experience rather than a broken one. The skeleton renders. The error state appears. The retry button works. None of that depends on whether openai.chat() returned a 200 or a 504.

import { Button, Card, Skeleton } from "@your-kit/ui"

export function ChatPane() {
  return (
    <Card>
      {loading && <Skeleton lines={3} />}
      {error  && <ErrorBanner retry />}
      {reply  && <Markdown source={reply} />}
      <Button onClick={send} disabled={loading}>Send</Button>
    </Card>
  )
}
Enter fullscreen mode Exit fullscreen mode

This is the durable layer. The model behind the send button can be swapped, retried, cached, or routed to a backup — and the user sees the same component, the same affordance, the same recovery path. When the same component renders identically on web, iOS, and Android from one API, your outage runbook shrinks to a single concern: is the model reachable? Not: does the dialog look right on three platforms?

Principle three: have a second path, pre-warmed

Provider outages are when failover earns its keep. The cheapest failover is a second provider you've already integrated but don't run on the hot path.

const PROVIDERS = [
  { name: "openai",    call: openai.chat,    weight: 1.0 },
  { name: "anthropic", call: anthropic.chat, weight: 0.0 }, // cold
] as const

async function chatWithFailover(prompt: string) {
  for (const p of PROVIDERS) {
    try {
      return { source: p.name, text: await p.call(prompt) }
    } catch (err) {
      logProviderFailure(p.name, err)
      continue
    }
  }
  throw new Error("all providers down")
}
Enter fullscreen mode Exit fullscreen mode

When the provider's status page flips red, flip the weight on the cold provider and ship the change. Or automate it: have your service watch the status feed and rebalance without a deploy.

This isn't theoretical. Multi-provider routing is how every team shipping serious AI features runs today. Single-provider dependence is a choice, not a constraint.

Monitoring: the cheapest insurance you'll ever buy

You can't recover from an outage you didn't notice. The first line of defence is knowing before your users do.

# the boring, correct answer
curl -fsS "$PROVIDER_STATUS_URL" | jq '.status.indicator'
Enter fullscreen mode Exit fullscreen mode

Wire that into a cron, a serverless function, or a hosted monitor. When indicator is anything other than none, post to Slack, page on-call, and raise a feature flag. Don't rely on social media — the provider's status page is the canonical source, even if it's slow to update.

// status-driven feature flag — flips your app into degraded mode automatically
const status = await fetch(process.env.PROVIDER_STATUS_URL!).then(r => r.json())
const degraded = status.status.indicator !== "none"
featureFlags.set("ai.chat.degraded", degraded)
Enter fullscreen mode Exit fullscreen mode

A 30-line script that flips a flag beats a 3,000-line runbook that someone has to read at 3am.

Caching: the failover nobody talks about

Most prompts are repeated. "Summarise this doc", "fix this typo", "explain this error" — the answers are stable enough that a 60-second cache absorbs a multi-minute outage invisibly.

import { LRUCache } from "lru-cache"

const cache = new LRUCache<string, string>({ max: 5_000, ttl: 60_000 })

async function chat(prompt: string) {
  const hit = cache.get(prompt)
  if (hit) return { source: "cache", text: hit }
  const reply = await openai.chat(prompt)
  cache.set(prompt, reply.text)
  return { source: "openai", text: reply.text }
}
Enter fullscreen mode Exit fullscreen mode

When the provider dies, repeat users don't notice. New users get the fallback path. The cache degrades the outage from "everything is broken" to "new requests are slow."

[[COMPARE: app coupled to a single provider vs app with a provider-agnostic UI layer]]

What this gets us

OpenAI going down isn't a story about OpenAI. It's a story about what you depend on.

  • The screen the user touches shouldn't care which provider answered.
  • The error path should be designed before the happy path, not after.
  • A second provider, a cache, and a status-driven flag are three lines of code each, and they buy you most of the headroom you need.
  • The UI layer is the part that doesn't churn when the model does.

When the next outage lands — and there will be one — the apps that survive are the ones whose UI was already provider-agnostic, whose fallbacks were already wired, and whose status monitors were already paging.

Closing

OpenAI's outage, like every outage before it, will get a post-mortem. Read it. Note the cause. Apply the lesson. Then go make sure your own stack doesn't have the same single point of failure.

The model is the part you rent. The UI is the part you own. Build accordingly.

Top comments (0)