Skip to main content
warpllm classifies a failure once and renders it the way OpenAI would have reported it. DeepSeek answers an exhausted balance with a 402; you see a 429 with code: "insufficient_quota". One handler covers every provider.
Normalizing, not forwarding. A failure warpllm classified is reported in OpenAI’s terms. A failure it did not classify keeps the upstream’s own answer — inventing one would be a guess in normalization’s clothing.

The two questions worth asking

Who failed?

Gateway — warpllm, its configuration, or the call into it. The provider was never reached or never asked. Retrying the identical call changes nothing.Provider — an upstream answered, and its answer was a failure.

Can waiting fix it?

A 429 covers a rate limit and an exhausted quota. Backing off clears the first and never clears the second. Only code tells them apart.

Catching errors

Error classes in the SDKs

Python and Node raise the classes the official OpenAI SDK raises, selected from the same status warpllm rendered. No binding interprets a status on its own.
Python inserts APIStatusError between APIError and the status-specific classes, so except APIStatusError catches everything an upstream answered while letting APIConnectionError through. Node has no such intermediate: every class extends APIError directly, and err.status === undefined distinguishes a connection failure.

Attributes on a caught error

The full taxonomy

Rust exposes warpllm’s own enum; the bindings receive the OpenAI rendering of it. warpllm_code appears verbatim on the gateway’s HTTP envelope and nowhere else.

warpllm’s own failures (origin: gateway)

The provider’s failures, normalized (origin: provider)

InvalidModel and ModelNotFound are different failures. The first fires before any request goes out — warpllm’s roster does not carry the name. The second is the provider saying it does not serve a model warpllm does carry, which usually means the roster needs an edit.The same split applies to MissingApiKey (no key at all, no request sent) versus Authentication (a key was sent and rejected).ProviderNotDeclared is a third neighbour, and only a client that declared its providers can produce it. It is deliberately not reported as a missing credential: telling you to export a key for a provider you chose not to serve would be exactly the wrong advice. What is wrong is the declaration, or the model string aimed at it. It is 400 rather than 404, which on a gateway would read as “no such endpoint”.

Retrying

warpllm does not retry for you yet, so today the policy is yours to write. The taxonomy exists to make that possible: every failure below says whether waiting can fix it.
A retry loop is planned, not ruled out — an untested retry policy shipped as a public contract is worse than none, so it lands once it is worked out rather than early. Failing over to a DIFFERENT model has shipped: name them in models, and warpllm moves past the failures this table marks as provider-scoped. See Model routing.
When an upstream sends Retry-After, warpllm preserves it: as a retry-after entry in the SDK error’s headers, and as a real HTTP header on the gateway’s response.

Errors during a stream

Once a stream is open, a failure arrives as an item on it rather than as a return value, and it is terminal — whatever produced it also ended the stream. The one distinction to keep: a stream that stops cleanly is a complete reply. A stream that stops early raises StreamTruncated after every chunk that did arrive, so you never mistake a cut-off answer for a finished one. See Streaming.

The gateway envelope

The HTTP gateway sends the standard OpenAI error body, so an official SDK pointed at it raises its own typed exception. warpllm adds fields on this surface only — an HTTP caller can ignore a field it does not recognize, which is not true of an attribute on an SDK error object.
string
"gateway" or "provider" — who the failure came from.
string
warpllm’s own taxonomy slug, from the tables above.
string
Which provider answered. Absent when none did.
integer
The upstream’s own status, before normalization.
string
The upstream’s own code, when it sent one.
A failure warpllm never reached a provider for has no OpenAI spelling — no client talking to OpenAI directly ever sees one. The gateway answers 502, which is what a gateway says when the thing behind it did not answer.

Next

Timeouts

The two deadlines, and which failure each one produces.

Streaming

How a stream ends, and how to tell complete from truncated.