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)
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 raisesStreamTruncated 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.