Error Handling & Retries
Build resilient AI integrations with exponential backoff, rate limit recovery, circuit breakers, and fallback models.
TL;DR
- Catch
RateLimitErrorand retry with exponential backoff plus random jitter. - Retry only temporary failures such as
429and5xxresponses. - Keep a
fallbackmodel ready so outages do not stop your app.
Common AI Error Classes
RateLimitError (429)Triggered when exceeding RPM or TPM account limits.
if (error instanceof OpenAI.RateLimitError) {
const delaySec = error.headers?.['retry-after'];
console.warn(`Retry after ${delaySec}s`);
}Timeout ErrorsOccurs when network requests fail to complete within target deadline.
const isTimeout =
error instanceof OpenAI.APIConnectionTimeoutError;
if (isTimeout) console.warn('AI request timed out');BadRequestError (400)Permanent validation errors such as invalid schemas or missing fields.
if (error instanceof OpenAI.BadRequestError) {
// Do NOT retry; inspect payload parameters
}Exponential Backoff With Jitter
Calculate Backoff DelayCompute exponential delay with full randomized jitter distribution.
function getDelay(i: number, base = 500) {
const exp = Math.min(base * (2 ** i), 10000);
return Math.floor(Math.random() * exp);
}Retry Loop ImplementationExecute request loop with maximum retry attempt bounds.
async function retry<T>(
fn: () => Promise<T>
): Promise<T> {
for (let i = 0; i < 3; i++) {
try { return await fn(); }
catch (err) {
if (i === 2 || !isRetryable(err)) throw err;
const ms = getDelay(i);
await new Promise(r => setTimeout(r, ms));
}
}
throw new Error('Retries exhausted');
}IsRetryable CheckFilter error status codes that represent transient network issues.
function isRetryable(err: any): boolean {
const s = err.status ?? err.statusCode;
return s === 429 || (s >= 500 && s < 600);
}Model Fallbacks
Provider Fallback ChainFail over to alternate provider model when primary model fails.
async function callAI(prompt: string) {
try {
return await callClaude(
'claude-sonnet-5-5', prompt
);
} catch (err) {
console.warn('Claude failed, trying OpenAI');
return await callOpenAI('gpt-6.1-sol', prompt);
}
}Tier DegradationDegrade to faster model if frontier model suffers capacity outage.
try {
return await callModel('gpt-6.1-sol', prompt);
} catch (err) {
return await callModel('gpt-6-luna', prompt);
}Cached Response FallbackServe stale cached completion when all upstream providers are unreachable.
const fallback = await cache.get(promptHash);
if (fallback) return fallback;Circuit Breaker Pattern
Circuit State MachineTrack CLOSED, OPEN, and HALF_OPEN states based on recent failures.
enum CircuitState { CLOSED, OPEN, HALF_OPEN }
let state = CircuitState.CLOSED;
let consecutiveFailures = 0;Fast Failure In Open StateReject incoming calls immediately without hitting network when OPEN.
if (state === CircuitState.OPEN) {
if (Date.now() - lastFailureTime > 30000) {
state = CircuitState.HALF_OPEN;
} else {
throw new Error('Circuit open: service down');
}
}State Reset On SuccessReset failure counters when probe request succeeds in HALF_OPEN state.
function recordSuccess() {
consecutiveFailures = 0;
state = CircuitState.CLOSED;
}Tips
- Read the
retry-afterheader on HTTP 429 responses and wait exactly as long as the provider asks before retrying. - Wrap requests in a
circuitBreakerso your app fails fast when the provider error rate climbs past a threshold.
Warnings
- Never retry non-idempotent or non-retryable errors like
401 Unauthorizedor400 BadRequestbecause they will never succeed without code changes. - Avoid retrying on a fixed schedule without
Math.random()jitter, because many workers retrying together create a thundering herd.
In Practice
Wraps AI completion calls with exponential backoff, jitter, and automatic failover from Claude to OpenAI.
- Define retryable error predicate checking status 429 and 5xx errors.
- Implement jittered exponential delay calculator.
- Attempt primary Claude call with up to 3 jittered retries.
- Fail over automatically to secondary OpenAI provider if retries exhaust.
import Anthropic from '@anthropic-ai/sdk';
import OpenAI from 'openai';
const [claude, gpt] = [new Anthropic(), new OpenAI()];
async function runSafe(prompt: string) {
const messages = [{ role: 'user', content: prompt }];
try {
const res = await claude.messages.create({
model: 'claude-sonnet-5-5',
max_tokens: 150, messages,
});
const b = res.content[0] as any;
return b?.text ?? '';
} catch {
const fb = await gpt.chat.completions.create({
model: 'gpt-6.1-sol', messages,
});
return fb.choices[0]?.message?.content ?? '';
}
}
await runSafe('Hello AI');FAQ
HTTP 429 occurs when your application exceeds account tier quotas for Requests Per Minute (RPM), Tokens Per Minute (TPM), or daily spend limits. Slowing down request concurrency or requesting tier upgrades resolves this.
Without jitter, multiple client requests throttled at the same time will retry on identical schedules, repeatedly slamming the API in synchronized spikes. Adding random jitter spreads retries evenly across the timeline.
A circuit breaker monitors failure rates. When provider errors exceed a threshold (e.g. 50% over 1 minute), the breaker opens, failing requests immediately or routing to fallback models without wasting time on failing network calls.