RoutingRetry & timeout
Retry & timeout
Exponential backoff defaults, per-call overrides, `CRTimeoutError`, and how it interacts with streaming.
The SDK retries transient failures and enforces a client-side timeout so a slow upstream doesn't pin your request handler forever.
Defaults
| Setting | Default |
|---|---|
| Retries | 3 attempts (2 retries on top of the initial call) |
| Status codes | 429, 502, 503, 504 |
| Backoff | baseDelayMs * 2^attempt + jitter |
| Backoff cap | 10_000 ms |
| Timeout | 60 s |
Retry-After | Honoured when present |
400/401/402/404/410 are not retried — they require code or
config changes, not waiting.
Configure once
import { createCleverRouter } from '@cleverrouter/sdk';
const cr = createCleverRouter({
apiKey: process.env.CLEVERROUTER_API_KEY!,
retry: {
maxAttempts: 5,
baseDelayMs: 250,
maxDelayMs: 8_000,
statusCodes: [429, 502, 503, 504],
},
timeoutMs: 30_000,
});Disable retries
const cr = createCleverRouter({
apiKey: process.env.CLEVERROUTER_API_KEY!,
retry: false,
});Per-call override
await cr.chat(
{ model: 'mistral/mistral-small-3.2', messages },
{ timeoutMs: 5_000 },
);CRTimeoutError
import { CRTimeoutError } from '@cleverrouter/sdk';
try {
await cr.chat(
{ model: 'mistral/mistral-small-3.2', messages },
{ timeoutMs: 1_000 },
);
} catch (err) {
if (err instanceof CRTimeoutError) {
console.warn(`Timed out after ${err.timeoutMs}ms`);
} else {
throw err;
}
}CRTimeoutError is distinct from CRProviderDownError (upstream
502/503/504) and AbortError (you called controller.abort()).
AbortSignal
Every helper accepts { signal }:
const ctrl = new AbortController();
const promise = cr.chat(
{ model: 'mistral/mistral-small-3.2', messages },
{ signal: ctrl.signal },
);
setTimeout(() => ctrl.abort(), 2_000);
try {
await promise;
} catch (err) {
if ((err as Error).name === 'AbortError') {
// user-initiated cancel
}
}Retry meets stream
Streams don't retry
Once the first chunk has shipped, the SDK can't replay a stream
on a different attempt. Instead, cr.stream() reconnects up to
twice with Last-Event-ID if the connection drops before
[DONE]. See Streaming.
A reasonable default
timeoutMs: 30_000for chat completions on standard models.timeoutMs: 120_000forreasoning_effort: 'high'calls — they routinely take 20–90s.maxAttempts: 3is the sweet spot. More retries don't help when the upstream is genuinely down; they just delay surfacing the failure to the user.- Don't retry
4xx(the SDK already doesn't). A retried401is still going to fail.
Related
- Errors — which statuses retry, which don't.
- Rate limits — what triggers
429. - Streaming — reconnect vs retry.