Azure OpenAI 503 Service Unavailable — regional outage handling
Regional outages are the largest single source of Azure OpenAI production incidents. Detecting them fast and failing over cleanly separates SLO-hitting teams from the rest.
Quick fix (TL;DR)
Real error messages you'll see
These are the exact strings returned by the Azure OpenAI service and its SDKs when this error occurs. Copy-paste-searching any of them should land on this page.
openai.APIStatusError: Error code: 503 - {'error': {'code': 'ServiceUnavailable', 'message': 'The service is currently unavailable. Please retry after some time.'}}openai.APITimeoutError: Request timed out. Caused by: httpx.ReadTimeout
openai.APIStatusError: Error code: 500 - {'error': {'code': 'InternalServerError', 'message': 'The server encountered an error and could not complete your request.'}}
Reference
Azure OpenAI outage detection signals
| Signal | How to check | When it triggers |
|---|---|---|
| Azure Service Health alert | Portal → Service Health → Health advisories | Microsoft-confirmed incident |
| status.azure.com | HTTP or RSS feed | Public-facing incidents |
| Elevated 5xx rate | App-level metrics | Any degradation, before Microsoft confirms |
| Elevated p99 latency | App-level metrics | Early warning — slowness precedes failures |
| Cross-region difference | Compare region metrics | Regional issue vs global |
Retry vs failover — decision matrix
| Error type | Retry same region | Failover to other region |
|---|---|---|
| 429 (rate limit) | Yes — honor retry-after | Only if retry-after > 30s |
| 500 / 503 (server) | Yes — 1-2 times with backoff | On 3rd failure |
| Timeout | Yes — with fresh connection | On 2nd timeout in 60s |
| 400 / 401 / 404 | No — bad request | No — same result in other region |
| Content filter | No — semantic issue | No — same filter policy |
Root causes, ranked by frequency
Based on developer reports across Azure OpenAI SDK forums, GitHub issues, and Microsoft Q&A during 2025–2026.
- 28%Regional infrastructure incident. Azure OpenAI in one region is degraded; requests to other regions unaffected. Duration typically 10-60 minutes.
- 18%Model capacity exhaustion. New popular model (e.g. GPT-5 launch) saturates capacity in a region; team assumes their subscription is affected but everyone is.
- 14%Downstream Azure dependency. Storage, networking, or auth incident in the region ripples into OpenAI. Fix comes from Microsoft.
- 12%Client bug looks like outage. Connection pooling misconfiguration exhausts sockets — every request times out but the service is healthy from other clients.
- 10%Network path issue. Client's network to the AOAI region degrades — DDoS, BGP incident, ISP peering. Non-Azure clients see it, Azure-hosted clients do not.
- 7%Regional deployment misconfigured. Deployment scaled down or SKU changed accidentally — the "outage" is self-inflicted infra change.
- 6%Retry storm from other tenants. During a partial degradation, retry storms from co-tenants make things worse. Manifests as intermittent 503.
- 5%Extended thinking / reasoning under load. o1/o3 requests taking longer during peak hours — client timeouts fire, requests actually completing.
Fixes — copy-paste solutions
Subscribe to Azure Service Health alerts for AOAI
Set up Service Health alerts scoped to Azure OpenAI, in the regions you use. Route to your on-call channel (PagerDuty, Opsgenie, Slack). This surfaces incidents before your dashboards do.
# 1) Create an action group to receive alerts az monitor action-group create \ --name aoai-oncall \ --resource-group my-rg \ --short-name aoai-oc \ --action email primary you@example.com \ --action webhook pagerduty https://events.pagerduty.com/integration/xxx/enqueue # 2) Create Service Health alert rule for Azure OpenAI in your regions az monitor activity-log alert create \ --name aoai-service-health \ --resource-group my-rg \ --scope /subscriptions/$SUB_ID \ --condition category=ServiceHealth \ --condition properties.impactedServices[*].ServiceName="Azure OpenAI" \ --condition properties.impactedServices[*].ImpactedRegions[*].RegionName="East US" \ --condition properties.impactedServices[*].ImpactedRegions[*].RegionName="West Europe" \ --action-group aoai-oncall \ --description "Alert on any Azure OpenAI incident in East US or West Europe" # 3) Also create a health advisory alert (planned maintenance, retirements) az monitor activity-log alert create \ --name aoai-health-advisory \ --resource-group my-rg \ --scope /subscriptions/$SUB_ID \ --condition category=ServiceHealth \ --condition properties.incidentType=HealthAdvisory \ --condition properties.impactedServices[*].ServiceName="Azure OpenAI" \ --action-group aoai-oncall
az monitor activity-log alert test or trigger a synthetic notification.Multi-region client with health-scored failover
Maintain a client per region. Score each region on error rate and latency. Route the next request to the healthiest region. Sinks bad regions in seconds; restores them automatically as they recover.
import time import random from collections import deque from openai import AzureOpenAI, APIError class RegionalClient: def __init__(self, name: str, endpoint: str, api_key: str, deployment: str): self.name = name self.deployment = deployment self.client = AzureOpenAI( api_key=api_key, azure_endpoint=endpoint, api_version="2024-10-21", ) self.window = deque(maxlen=100) # (timestamp, was_error) self.sink_until = 0 def score(self) -> float: """0.0 = broken, 1.0 = perfect.""" if time.time() < self.sink_until: return 0.0 if not self.window: return 1.0 errors = sum(1 for _, e in self.window if e) return 1.0 - (errors / len(self.window)) def record(self, was_error: bool): self.window.append((time.time(), was_error)) # Sink for 30s after 5 errors in 100 requests if sum(1 for _, e in list(self.window)[-100:] if e) >= 5: self.sink_until = time.time() + 30 class MultiRegionClient: def __init__(self, regions: list[RegionalClient]): self.regions = regions def _pick(self) -> RegionalClient: """Choose the highest-scoring region, break ties randomly.""" scores = [(r.score(), random.random(), r) for r in self.regions] scores.sort(reverse=True) return scores[0][2] def chat(self, messages: list, max_attempts: int = 3): errors = [] for attempt in range(max_attempts): r = self._pick() try: resp = r.client.chat.completions.create( model=r.deployment, messages=messages, max_tokens=500, timeout=15.0, ) r.record(False) return resp, r.name except APIError as e: r.record(True) errors.append((r.name, str(e))) if hasattr(e, "status_code") and e.status_code in {400, 401, 404}: raise # non-transient raise RuntimeError(f"All regions failed: {errors}") # Setup mc = MultiRegionClient([ RegionalClient("eastus", "https://aoai-eastus.openai.azure.com", "key1", "gpt4o-eastus"), RegionalClient("westeurope", "https://aoai-westeurope.openai.azure.com", "key2", "gpt4o-westeurope"), RegionalClient("sweden", "https://aoai-sweden.openai.azure.com", "key3", "gpt4o-sweden"), ]) resp, served_by = mc.chat([{"role": "user", "content": "hi"}]) print(f"Served by {served_by}")
Exponential backoff with jitter — proper retry
Retry with exponential backoff plus random jitter to avoid thundering-herd behaviour when a region recovers. Cap at 3 attempts and let higher layers (the multi-region client) handle the rest.
import time import random from typing import Callable, TypeVar from openai import APIError, RateLimitError, APITimeoutError T = TypeVar("T") def with_retry( fn: Callable[[], T], max_attempts: int = 3, base_delay: float = 1.0, max_delay: float = 30.0, ) -> T: """Call fn() with exponential backoff and full jitter on transient errors.""" for attempt in range(1, max_attempts + 1): try: return fn() except (RateLimitError, APITimeoutError) as e: transient = True # Honor retry-after if provided retry_after = None if hasattr(e, "response") and e.response is not None: ra = e.response.headers.get("retry-after") retry_after = float(ra) if ra else None except APIError as e: transient = getattr(e, "status_code", 0) in {500, 502, 503, 504, 529} retry_after = None if not transient: raise # non-transient — do not retry if attempt == max_attempts: raise # Exponential backoff with full jitter (better than "equal jitter") max_wait = min(max_delay, base_delay * (2 ** (attempt - 1))) wait = retry_after or random.uniform(0, max_wait) time.sleep(wait) # Usage from openai import AzureOpenAI client = AzureOpenAI(api_key="...", azure_endpoint="...", api_version="2024-10-21") def call(): return client.chat.completions.create( model="gpt4o-prod", messages=[{"role": "user", "content": "..."}], max_tokens=200, timeout=15, ) response = with_retry(call, max_attempts=3, base_delay=1.0, max_delay=10.0)
Prevention checklist
Ship these seven safeguards once and this error stops appearing in your logs.
- Provision Azure OpenAI in at least two independent regions from day one — retrofitting during an outage is not viable.
- Set up Service Health alerts routed to your on-call rotation, and test the pipeline monthly.
- Track error rate and p99 latency per region as SLIs; alert when either deviates by 3× the 7-day baseline.
- Configure client timeouts to 15-30s for chat, 60-90s for reasoning models — do not use SDK defaults.
- Cap retries at 3 attempts, use full jitter, always honour retry-after headers.
- Publish a runbook: "AOAI 5xx spike" → check Service Health → check per-region metrics → confirm failover working.
- Practice a regional failover monthly in staging (chaos engineering) — teams that never practice fail during real incidents.
Frequently asked questions
Related errors & hubs
Get the weekly AI-error digest
New fixes, provider status recaps, and one deep tutorial — every Tuesday. 8,400+ engineers.