Three AI providers failed inside one 90 minute window and no shared cause was ever confirmed. Why a second vendor is not a second failure domain, and what to build instead.
On the morning of September 3, 2026, three of the largest AI providers reported outages inside roughly the same 90-minute window.
According to each company's public status records, compiled in reporting on the incident, Anthropic logged elevated errors on Claude from 6:23 am Pacific, resolved at 9:16. xAI's Grok went down at 6:30 and was restored at 10:05. OpenAI's ChatGPT and Codex were unavailable from 7:43 to 8:17, a 34-minute window the company attributed to a routing error.
Google's Gemini, running on Google Cloud, stayed largely upright.
Cursor, which depends on multiple model providers, was also affected.
That last detail is the one worth examining. Cursor had done what every architecture guide recommends. It did not depend on a single vendor. It still had a bad morning.
Less than the internet suggests, and the gap matters for anyone drawing conclusions.
OpenAI pointed to a routing error. SpaceX, xAI's parent, stated publicly that Grok's downtime stemmed from an outage at its Memphis compute center, and apologised to what it described as its impacted compute partners. Anthropic said it had identified a cause but did not publish it.
A theory circulated rapidly that a single Microsoft Azure East US failure took all three down. That claim deserves care. Per analysis of the public incident record citing WIRED's reporting, no matching public incident at Azure, AWS, or Cloudflare accounted for all three events when checked. That weakens the single-hyperscaler explanation. It does not prove the incidents were independent.
There is also a mundane amplification mechanism, raised in Computing's coverage: when ChatGPT went down, which drew by far the largest share of user reports, users moved to Claude and Grok. Load shifting alone can turn one provider's incident into three providers having a bad morning with no shared infrastructure involved.
The honest summary: three major providers failed close together, the public record establishes neither a common failure nor independence.
The SpaceX detail is what should change architectural thinking here.
Anthropic and xAI announced a compute partnership with SpaceX in May 2026. When SpaceX apologised to its impacted compute partners following a Memphis compute center outage, it indicated that at least one of its AI customers was affected by that same event.
Whether or not this explains September 3, it demonstrates something structural. Providers can share data center suppliers, network routes, identity services, safety services, and compute partners without any of those relationships surfacing on a status page you monitor.
Our read: the lesson is not about Azure. A dependency map built from vendor names is incomplete by construction, because the coupling that matters sits a layer below the vendor and is not published. Adding a second provider to an architecture diagram tells you nothing about whether you added a second failure domain.
A multi-vendor setup means more than one contract. A multi-failure-domain setup means no single upstream event can remove all your options simultaneously. Only the second property keeps a product running.
Gemini's survival on September 3 is the clearest available evidence that genuinely distinct infrastructure behaves differently under correlated stress. It is also unsatisfying, because the property protecting you belongs to someone else's infrastructure decisions, which you neither control nor can fully inspect.
There is a practical version that helps. The same model family is frequently reachable through more than one path:
| Capability | Path A | Path B | Distinct domain? |
|---|---|---|---|
| Claude models | Anthropic API | AWS Bedrock | Yes, different hosting and network path |
| Gemini models | Google AI API | Google Vertex AI | Partially, shared provider |
| OpenAI models | OpenAI API | Azure OpenAI Service | Partially, disputed coupling |
| Open-weight models | Managed inference host | Self-hosted GPU | Yes, fully separate |
Routing the same model through a second path preserves output quality in a way that switching model families does not, which matters when your prompts and evals are tuned to one model's behaviour.
To be fair to all three providers: a 34-minute routing error and a three-hour infrastructure incident are unremarkable by infrastructure standards. Every serious platform company has both on its record. The problem is not that they failed. It is that the coupling between them is invisible to the teams building on top.
A naive fallback list makes outages worse by retrying into a saturated provider. A domain-aware fallback with a circuit breaker does not.
import time
from dataclasses import dataclass, field
from enum import Enum
class State(Enum):
CLOSED = "closed" # healthy, traffic flows
OPEN = "open" # failing, skip immediately
HALF_OPEN = "half_open" # probing recovery
@dataclass
class Breaker:
"""Per-path circuit breaker. One instance per failure domain."""
failure_threshold: int = 4
cooldown_seconds: int = 30
failures: int = 0
opened_at: float = 0.0
state: State = State.CLOSED
def allow(self) -> bool:
if self.state is State.OPEN:
if time.monotonic() - self.opened_at >= self.cooldown_seconds:
self.state = State.HALF_OPEN
return True
return False
return True
def record_success(self) -> None:
self.failures = 0
self.state = State.CLOSED
def record_failure(self) -> None:
self.failures += 1
if self.failures >= self.failure_threshold:
self.state = State.OPEN
self.opened_at = time.monotonic()
@dataclass
class Path:
name: str
domain: str # the failure domain, not the vendor
call: callable
breaker: Breaker = field(default_factory=Breaker)
def invoke(paths: list[Path], prompt: str, timeout_s: float = 8.0):
"""Try paths in order, skipping domains already known to be failing."""
tried_domains: set[str] = set()
for path in paths:
if path.domain in tried_domains:
continue # same domain already failed, skip it
if not path.breaker.allow():
tried_domains.add(path.domain)
continue
try:
result = path.call(prompt, timeout=timeout_s)
path.breaker.record_success()
return result, path.name
except Exception:
path.breaker.record_failure()
tried_domains.add(path.domain)
return None, None # every path exhausted: degrade, do not spin
Two details carry most of the value. The domain field, not the vendor name, is what deduplication keys on, so two paths sharing an upstream are not both attempted during a correlated failure. And an aggressive timeout matters more than the retry count: a 60-second default timeout turns a provider incident into a thread-pool exhaustion incident in your own service.
September 3 is the scenario where every call fails. The design question is not which provider you switch to. It is what the feature does.
def summarise_ticket(ticket, paths):
result, used_path = invoke(paths, build_prompt(ticket))
if result is not None:
cache.set(f"summary:{ticket.id}", result, ttl=86_400)
return {"summary": result, "source": used_path}
# Degradation ladder, in preference order
cached = cache.get(f"summary:{ticket.id}")
if cached:
return {"summary": cached, "source": "cache", "stale": True}
if ticket.is_queueable:
enqueue_for_retry(ticket.id)
return {"summary": None, "status": "queued"}
# Deterministic, non-AI fallback beats an empty state
return {
"summary": extractive_summary(ticket.body, sentences=3),
"source": "extractive",
"degraded": True,
}
Work through this with whoever owns your infrastructure:
Failover architecture is burst-shaped. It is genuine engineering, clearly bounded, matters enormously a few times a year, and belongs to nobody on a ten-person team.
It also rarely gets built well under pressure, because the day you need it is the day you have no time to build it. Teams that got through September 3 without a support queue had decided months earlier what their product does when the model does not answer.
This is the surface area problem we described in our analysis of AI-expanded scope. AI introduced a dependency into your critical path that you do not operate, cannot inspect, and share with competitors. It deserves the treatment you would give a database: a known failure mode, a tested fallback, and an explicit decision about behaviour when it is unavailable. It also compounds the agent permission work covered in our edition on agent-inflicted damage, since an agent mid-task when its model becomes unavailable is its own failure mode.
The engineers we embed at Percime Technologies do this work directly: dependency mapping, failover paths, timeout and retry behaviour, and graceful degradation. The parts of a system worth nothing on a normal Tuesday and worth the quarter on the one morning three providers have trouble at once.
Cursor had more than one provider. The open question is whether it had more than one failure domain.
© 2026 Percime Technologies. All rights reserved.