SDK
Errors
The type and code names Conifer returns on the OpenAI-compat wire. The official SDKs already handle these names.
Catch the official client’s error class, then branch on status and code.
import OpenAI, { APIError } from "openai";
try {
await client.chat.completions.create({ /* … */ });
} catch (err) {
if (err instanceof APIError) {
console.error(err.status, err.code, err.error);
}
throw err;
}from openai import APIError, AuthenticationError, NotFoundError, RateLimitError
try:
client.chat.completions.create(...)
except AuthenticationError as e:
print("401", e.code)
except NotFoundError as e:
print("404", e.code, e.body)
except RateLimitError as e:
print("429", e.code)
except APIError as e:
print(e.status_code, e.code, e.body)OpenAI-compat
These names apply on /v1/chat/completions, /v1/completions and /v1/responses. On a stream, the in-band error object has the same shape as the buffered envelope.
| Status | type | code | Meaning |
|---|---|---|---|
| 401 | invalid_request_error | invalid_api_key | The message is Incorrect API key provided and the header is WWW-Authenticate: Bearer. Every auth failure returns this one 401, whether the key is missing, mistyped, expired or revoked. |
| 402 | insufficient_allowance | insufficient_quota | Billing is not set up, or the balance cannot cover the request. Nothing is charged. It stays a 402 and is not remapped to 429. |
| 402 | cost_ceiling_exceeded | stays type | The request could have cost more than the ceiling you set in x-conifer-max-cost-nanousd. Nothing is charged. |
| 402 | key_spend_cap_exceeded | stays type | This key has reached its own spend cap. The account may be funded and other keys still work, so adding credit does nothing here. Rotate the key or raise its cap. The body names key_id, cap_nanodollars and would_charge_nanodollars, and spent_nanodollars when it is known. There is no code, so branch on type. |
| 409 | request_in_progress | stays type | The same idempotency key was sent twice. If the message ends “retry shortly”, the first attempt is still finishing and a retry is safe; it cannot bill twice. “Already used with a different request body” means change the key or the body. |
| 429 | rate_limit_error | rate_limit_exceeded | The header is Retry-After: 1. Conifer does not send invented x-ratelimit-* figures. |
| 404 | model_not_found | model_not_found | The param is model. The id is not in the catalog your key can call. Close matches, if any, are listed in error.suggestions. |
| 400 | invalid_request_error | context_length_exceeded | The prompt is over the model's context window. Some providers report this themselves, and that relays as a 422 upstream_error naming the token counts. Other 400s also say what is wrong, for example tools sent to a model that does not declare them. Capability refusals stay 400 and are not remapped to 402 or 429. |
Capability refusals
Some 400s come from asking a model for something it does not do, such as image content on a model without the vision cap or tools on a model without tools. A different model would serve the same request. The Conifer SDK types this as ConiferCapabilityError, the request is not billed, and the TypeScript fallback chain moves on to the next model.
Anthropic /v1/messages
Error types on this route follow Anthropic’s shape, so the official Anthropic clients read them unchanged.
Request ids
request-id and x-request-id carry the same value as x-conifer-request-id. Quote it when you write in about a request.
What each status means
Every auth failure is one 401. A 402 means billing or a spend ceiling, never a missing tool or modality. A 429 is rate limit only. Capability mismatches stay 400 and name the problem in the body. Responses carry no OpenRouter provider metadata and no invented remaining-quota figures on x-ratelimit-* headers.
Bodies
{
"error": {
"message": "Incorrect API key provided",
"type": "invalid_request_error",
"code": "invalid_api_key"
}
}{
"error": {
"type": "insufficient_allowance",
"code": "insufficient_quota",
"docs_url": "https://conifer.build/console#/billing"
}
}{
"error": {
"type": "cost_ceiling_exceeded"
}
}{
"error": {
"type": "key_spend_cap_exceeded",
"key_id": "…",
"cap_nanodollars": 5000000000,
"spent_nanodollars": 4900000000,
"would_charge_nanodollars": 200000000
}
}{
"error": {
"type": "rate_limit_error",
"code": "rate_limit_exceeded"
}
}{
"error": {
"code": "model_not_found",
"param": "model"
}
}{
"error": {
"type": "invalid_request_error",
"code": "context_length_exceeded"
}
}