OpenAI API 500 Internal Server Error — How to Fix It
A 500 Internal Server Error from the OpenAI API (along with 502 Bad Gateway and 503 Service Unavailable) is an OpenAI-side failure — not your key, not your code. These are transient and safe to retry with backoff. This guide shows how to tell a real outage apart from a bad request, and how to build retry logic that keeps your app running.
What does a 500 (or 502 / 503) mean from OpenAI?
A 500 Internal Server Error means “the server had an error while processing your request.” A 503 Service Unavailable means OpenAI is overloaded or temporarily down, and a 502 Bad Gateway means an upstream proxy failed. All three are OpenAI-side and retriable. They are fundamentally different from client errors like 400 and 401:
| Code | Meaning | Fix |
|---|---|---|
| 400 | Your request is malformed (bad params, invalid schema) | Fix the input — do NOT retry |
| 401 | Invalid or missing API key | Check key at platform.openai.com |
| 429 | Your rate limit or quota is exhausted | Back off, or add credit / raise limits |
| 500 / 502 / 503 | OpenAI's servers failed or are overloaded | Retry with exponential backoff |
One nuance: a 500 is usually OpenAI's fault, but it can occasionally be triggered on your side by a malformed streaming request or an extremely large payload (a giant prompt or a pathological max_tokens). If a 500 reproduces on one specific request while everything else works, treat that request as the suspect (see step 4). A 500 across all your requests is an OpenAI incident.
5 steps to fix and prevent 500 errors
Check whether it is an OpenAI outage
Before writing any retry code, find out whether OpenAI is having a platform-wide problem or whether your 500s are isolated to one request. The fastest signal is the Prismix status dashboard, which polls the OpenAI API and aggregates incidents in real time.
Diagnosis checklist
- Open prismix.dev/service/openai — an active incident appears at the top with a timeline.
- A wave of 500s across many users or many of your requests = OpenAI-side incident. Back off and wait.
- Only one request returns 500 while others succeed? That is a payload problem, not an outage — skip to step 4.
- Also check status.openai.com for OpenAI's own incident page.
Let the SDK retry automatically (simplest fix)
The official openai Python and Node SDKs automatically retry connection errors and 5xx responses (including 500, 502, and 503) with exponential backoff. The default is 2 retries — raise it for production workloads:
# Python — increase SDK retries to 5 (covers most transient 500/502/503 spikes)
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["OPENAI_API_KEY"],
max_retries=5, # default is 2; the SDK retries 5xx and connection errors
timeout=60.0, # seconds per attempt
)
resp = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
) // TypeScript — same pattern
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
maxRetries: 5,
timeout: 60_000, // ms
});
With max_retries=5, the SDK waits roughly 1 s, 2 s, 4 s, 8 s, and 16 s (with jitter) before surfacing the error. This alone resolves the vast majority of short 500 waves with zero extra code.
Add explicit exponential backoff for batch jobs
For batch processing or long-running server jobs, write an explicit retry wrapper. It should retry only the server-side 5xx codes, cap the wait time, add jitter, and re-raise client errors (400/401) immediately so a broken request does not loop forever:
import os
import time
import random
from openai import OpenAI, APIStatusError
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"], max_retries=0)
RETRIABLE = {500, 502, 503} # server-side only
def create_with_backoff(max_attempts: int = 6, **kwargs):
"""Retry OpenAI 5xx server errors with exponential backoff + jitter."""
for attempt in range(max_attempts):
try:
return client.chat.completions.create(**kwargs)
except APIStatusError as e:
if e.status_code not in RETRIABLE:
raise # 400/401/429 are not server faults — handle separately
if attempt == max_attempts - 1:
raise
wait = min(60.0, (2 ** attempt) + random.uniform(0, 1))
print(f"OpenAI {e.status_code} server error. Retry {attempt + 1}/{max_attempts - 1} in {wait:.1f}s")
time.sleep(wait)
# Usage
response = create_with_backoff(
max_attempts=6,
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
) // TypeScript async version — catch OpenAI.APIError and check err.status
import OpenAI from 'openai';
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, maxRetries: 0 });
const RETRIABLE = new Set([500, 502, 503]);
async function createWithBackoff(
params: OpenAI.ChatCompletionCreateParamsNonStreaming,
maxAttempts = 6,
) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
return await client.chat.completions.create(params);
} catch (e) {
if (!(e instanceof OpenAI.APIError) || !RETRIABLE.has(e.status ?? 0)) throw e;
if (attempt === maxAttempts - 1) throw e;
const wait = Math.min(60_000, (2 ** attempt) * 1000 + Math.random() * 1000);
console.warn(`OpenAI ${e.status} — retry ${attempt + 1} in ${(wait / 1000).toFixed(1)}s`);
await new Promise(r => setTimeout(r, wait));
}
}
} Reduce payload & avoid pathological inputs
If a 500 reproduces on one specific request while the rest of your traffic succeeds, it is almost certainly the request, not an outage. An extremely large prompt, a huge max_tokens, or a malformed function / tool schema can push the backend into an internal error instead of a clean 400. Shrink the request and stream long outputs to isolate the trigger:
# A single giant, buffered request is the most common self-inflicted 500.
# Prefer smaller, streamed calls over one enormous completion.
stream = client.chat.completions.create(
model="gpt-4o",
messages=messages,
max_tokens=1024, # avoid pathologically large output caps
stream=True, # stream long output instead of buffering it all
)
for chunk in stream:
delta = chunk.choices[0].delta.content or ""
print(delta, end="") - Validate tool/function schemas: a malformed
toolsdefinition can surface as a 500 rather than a 400 — double-check the JSON Schema. - Trim the prompt: if you are near the model's context window, a 500 can appear before you hit a clean context-length error. Cut input tokens and retry.
- Test the request in isolation: replay the exact failing payload with a tiny
max_tokens. If it still 500s alone, the payload is the cause.
Add a circuit breaker for production
During a prolonged 500/503 incident, retrying every request just queues up a backlog and hammers a backend that is already failing. A circuit breaker stops sending requests after a failure threshold, waits a fixed recovery window, then probes once before resuming:
import time
from openai import APIStatusError
class CircuitBreaker:
def __init__(self, failure_threshold=3, recovery_timeout=60):
self.failures = 0
self.threshold = failure_threshold
self.timeout = recovery_timeout
self.opened_at = None
self.state = "closed" # closed | open | half-open
def record_failure(self):
self.failures += 1
if self.failures >= self.threshold:
self.state = "open"
self.opened_at = time.monotonic()
print(f"Circuit OPEN — pausing OpenAI calls for {self.timeout}s")
def record_success(self):
self.failures = 0
self.state = "closed"
def allow_request(self) -> bool:
if self.state == "closed":
return True
elapsed = time.monotonic() - self.opened_at
if elapsed >= self.timeout:
self.state = "half-open"
return True # probe request
return False
breaker = CircuitBreaker()
def safe_create(**kwargs):
if not breaker.allow_request():
raise RuntimeError("OpenAI circuit open — skipping request")
try:
result = client.chat.completions.create(**kwargs)
breaker.record_success()
return result
except APIStatusError as e:
if e.status_code in (500, 502, 503):
breaker.record_failure()
raise Get an email the next time OpenAI goes down
Outage alerts for OpenAI, straight to your inbox. No account needed, unsubscribe in every email.
Watching more than one service? A free account covers 5 services + a daily digest — and Pro is currently free.
FAQ
Is a 500 my fault or OpenAI's?
Almost always OpenAI's. A 500 Internal Server Error means “the server had an error while processing your request” — a backend failure, not a bad key or bad input. The rare exception is a malformed streaming request or an extremely large payload, which can surface as a 500 instead of a 400. If a 500 reproduces on one specific request only, inspect that request; if it hits everything, it is an OpenAI incident.
Should I retry a 500?
Yes — with exponential backoff. 500, 502, and 503 are transient server errors, and the openai SDK already retries them for you. Do not blindly retry a 400 (malformed request) or 401 (bad key): those fail identically on every attempt until you fix the request or the credentials.
What is the difference between a 500 and a 429?
A 429 means your account exceeded a rate limit or ran out of quota/credit — the request was rejected before reaching the model. A 500/502/503 means OpenAI's servers failed on an otherwise valid request. Both can be retried with backoff, but a 429 also needs you to slow down or add credit, while a 500 usually clears on its own.
Am I charged for a 500?
No. A failed request that returns 500/502/503 is not billed — you only pay for successful completions. If a streaming response fails partway through, you are billed only for tokens actually delivered before the error, which is typically zero on an upstream failure.
How long do OpenAI 500 waves last?
Usually just minutes — a short spike clears once OpenAI's backend recovers. Bigger infrastructure incidents can run 30–120 minutes. Check prismix.dev/service/openai for the incident timeline and status.openai.com for OpenAI's own updates. If an incident is active, back off hard rather than retrying tightly.