Azure OpenAI Outage: Keep Your AI App Running A developer outlined a resilience pattern for LLM-dependent applications after Azure OpenAI Service, Foundry Agent Service, Foundry Models and Cognitive Services in the Sweden Central region failed for 5 hours and 55 minutes on 29 September 2026, causing intermittent request failures, elevated latency and HTTP 5XX errors on model and data plane APIs. The approach combines per-call timeouts, a per-provider circuit breaker that stops traffic to a failing dependency during a cool-down, and an ordered fallback list across providers or regions, on the argument that retries alone worsen a multi-hour outage by piling up workers and flooding the provider on recovery. Treat your LLM provider like any dependency that will eventually fail. Set strict timeouts, stop calling a provider that is clearly down with a circuit breaker, and keep a second route ready, whether that is another region, another provider or an honest degraded mode. Retries alone will not save you from an outage that lasts six hours. According to a postmortem write up by Artur Markus https://www.arturmarkus.com/postmortem-azures-sweden-central-ai-outage-and-the-18-region-gateway-failure-24-hours-later/ , Azure OpenAI Service, Foundry Agent Service, Foundry Models and Cognitive Services in the Sweden Central region failed for 5 hours and 55 minutes on 29 September 2026, from 10:03 to 15:58 UTC. Customers saw intermittent request failures, higher latency and HTTP 5XX errors against model and data plane APIs. Every cloud and model provider has incidents at some point, so this is not a post about blame. It is about how to engineer for the day it happens to you. The same write up mentions a separate networking incident the next day across several regions, but I am not drawing conclusions from it because those details have not been confirmed. For the confirmed record, the official Azure status history https://azure.status.microsoft/en-us/status/history is the place to look, rather than anyone’s summary, including mine. The details of this incident matter less than its shape. A model API in one region was unusable for most of a working day. If your product calls an LLM on the critical path, that is your outage too, and the question is what your code does during hour three. Retries are built for blips: a dropped connection, a single overloaded node. Against a six hour incident they do harm. Every user request waits through several failed attempts, your workers pile up, your queue grows, and when the provider recovers it receives a flood. The first step is to stop treating all errors the same. I sort them into three groups, because each one has a different correct reaction. Without a timeout, an LLM call can hang for minutes and hold a connection, a worker and a user. Set one on every call, and choose it from what a healthy call looks like for your use case. A short classification should answer in a few seconds. A long generation needs a longer limit, and when you stream it is worth watching time to first token separately, because that is the number that tells you the provider is struggling. js const isOutage = error = error.name === 'TimeoutError' || error.code === 'ECONNRESET' || error.status = 500 && error.status < 600 export async function callProvider provider, messages { const signal = AbortSignal.timeout provider.timeoutMs return provider.chat messages, { signal } } A circuit breaker remembers that a dependency is failing and stops sending it traffic for a while. That protects your users from waiting on a dead service and protects the service from a retry storm when it comes back. export class CircuitBreaker { constructor { threshold = 5, coolDownMs = 30000 } = {} { this.threshold = threshold this.coolDownMs = coolDownMs this.failures = 0 this.openUntil = 0 } allows now = Date.now { return now = this.openUntil } success { this.failures = 0 this.openUntil = 0 } failure now = Date.now { this.failures += 1 if this.failures = this.threshold { this.openUntil = now + this.coolDownMs } } } After the cool down, allows returns true again and the next request acts as a probe. If it fails, the failure count is still above the threshold, so the breaker opens again straight away. If it succeeds, everything resets. That is the whole idea, and it is enough for most services. With timeouts and a breaker per provider, the fallback is a loop over an ordered list. Each entry has a name, a chat function, a timeout and its own breaker. js export async function askWithFallback providers, messages { let lastError for const provider of providers { if provider.breaker.allows continue try { const reply = await callProvider provider, messages provider.breaker.success return { reply, provider: provider.name } } catch error { if isOutage error throw error provider.breaker.failure lastError = error } } throw lastError ?? new Error 'Every provider is unavailable' } Two cautions. A second region protects you from a regional failure like Sweden Central, but not from a problem that spans the provider, so for the critical path a second provider is the stronger choice. And a fallback model is not a drop in copy. Prompts that work well on one model can behave differently on another, so keep a small set of real examples and run it against every provider you list. A fallback you have never tested is a hope, not a plan. This is easier when your code never imports a vendor SDK directly. If each provider is an adapter behind one small interface, adding a second is an afternoon of work. The same design is used in the booking assistant described in An AI Booking Assistant That Cannot Double Book https://ahmadhsn1.github.io/blog/ai-booking-assistant-tool-calling/ . When every route is down, the worst outcome is a spinner that never ends. Decide in advance. Background work can go into a queue and run later. A chat can say plainly that the assistant is unavailable and offer another way to reach you. Something that has a cached answer can serve it, clearly marked. The Copper Larder chatbot takes this approach: when the model is down, rate limited or missing its key, every path still returns a warm, on brand message and a callback card, so a visitor can still leave a number. The design is covered in the Copper Larder case study https://ahmadhsn1.github.io/work/copper-larder/ . You do not need a real outage to rehearse one. Write a fake provider that returns a 503, another that hangs past its timeout, and a healthy one, then assert how your code behaves. python import test from 'node:test' import assert from 'node:assert' const broken = { name: 'broken', timeoutMs: 100, breaker: new CircuitBreaker { threshold: 1 } , chat: async = { throw Object.assign new Error 'down' , { status: 503 } }, } const healthy = { name: 'healthy', timeoutMs: 100, breaker: new CircuitBreaker , chat: async = 'ok', } test 'falls back when the first provider is down', async = { const result = await askWithFallback broken, healthy , assert.equal result.provider, 'healthy' assert.equal broken.breaker.allows , false } Run something like this in CI, and once in a while rehearse it for real by pointing a staging environment at a provider that always fails. You will find the one forgotten call that has no timeout. Originally published at ahmadhsn1.github.io https://ahmadhsn1.github.io/blog/azure-openai-outage-keep-ai-app-running/ .