Error reference

Every error the router can return, grouped by status code. Each entry shows the exact
body you receive. Messages in 'quotes' are filled in from your request.
Errors overview explains the three body formats and the order checks
run in.

Branch on status and code, not on message text

Messages are meant for humans and can change. Use the status code and, for 403, the
code and param fields. Messages forwarded from a provider are not part of the
Quantized contract at all.

400 Bad Request

License out of budget

{
  "error": {
    "message": "Your license is out of budget"
  }
}

No wallet on your license has budget left in the current period or lifetime limit. The
check runs before the request reaches a provider, so you are not charged. Check
budget in GET /v1/license and contact your institution to raise the
limit.

Endpoints: every endpoint that calls a provider (all POST endpoints).

Malformed X-Quantized-Provider header

{
  "error": {
    "message": "Malformed X-Quantized-Provider header: 'a/b/c'"
  }
}

The header must be a provider slug (openrouter) or <provider>/<connection>. An empty
part, or more than one /, is rejected.

Endpoints: all authenticated endpoints, including GET /v1/models and
GET /v1/license.

Invalid X-Quantized-Metadata header

The header must be a JSON object whose values are all scalars (string, number, boolean,
or null). There are three messages:

{
  "error": {
    "message": "X-Quantized-Metadata must be a valid JSON object"
  }
}
{
  "error": {
    "message": "X-Quantized-Metadata must be a JSON object"
  }
}
{
  "error": {
    "message": "X-Quantized-Metadata value for 'course' must be a scalar; nested objects/arrays are not allowed"
  }
}

The first is returned when the value is not valid JSON, the second when it is valid JSON
but not an object (for example [1]), and the third when a value is an object or array.

Endpoints: all authenticated endpoints.

No provider available

{
  "error": {
    "message": "No provider available for endpoint 'chat_completions' under the current pins"
  }
}

No provider your license can reach serves this endpoint. With an X-Quantized-Provider
pin, the message ends in under the current pins. Common causes:

  • you pinned a provider that doesn’t serve the endpoint (for example consensus on
    /v1/fetch) or that doesn’t exist;
  • you pinned a provider your license type doesn’t allow; the header can only narrow the
    providers your license allows, never widen them;
  • type: "academic" on /v1/web-search when your license can’t reach Consensus.

Without a pin, the message has no suffix:

{
  "error": {
    "message": "No provider available for endpoint 'image_generation'"
  }
}

That means no provider is configured for the endpoint on your license. Contact your
institution.

The endpoint name in the message is an internal name: chat_completions, responses,
embeddings, gemini_embeddings, bedrock_embeddings, image_generation,
web_search, or web_fetch.

Model doesn’t support the input or output modality

{
  "error": {
    "message": "No available provider for model 'openai/gpt-4.1-nano' supports audio input"
  }
}

Your chat request contains an image_url, input_audio, or video_url part that no
provider serving the model accepts. The router skips providers that can’t serve the
request and only fails when every reachable one refuses. On
/v1/images/generations the same check applies to output:

{
  "error": {
    "message": "No available provider for model 'openai/gpt-4.1-nano' supports image output"
  }
}

Pick a model whose input_modality / output_modality in GET /v1/models
includes what you need.

Endpoints: /v1/chat/completions, /v1/responses, /v1/images/generations.

Unsupported reasoning effort

{
  "error": {
    "message": "Model does not support reasoning effort 'xhigh'"
  }
}

The model has a fixed set of reasoning levels on the selected provider, and the effort
you asked for isn’t one of them. Use a lower effort, or "none" to disable reasoning.

Endpoints: /v1/chat/completions, /v1/responses.

previous_response_id not supported

{
  "error": {
    "message": "previous_response_id is not supported by this provider; send the full conversation in `input`."
  }
}

The request was served by a stateless provider (CheaperInference). Send the whole
conversation in input instead of chaining with previous_response_id.

Endpoints: /v1/responses.

Request blocked by guardrail

{
  "error": {
    "message": "Request blocked by guardrail"
  }
}

A guardrail configured by your institution blocked the request. An input guardrail runs
before the provider call, so you are not charged. An output guardrail runs on the
provider’s response, so the call is charged. Streamed responses are never blocked by
output guardrails, because the response has already been sent.

Endpoints: every endpoint that calls a provider.

Provider connection misconfigured

{
  "error": {
    "message": "Connection 'default' is misconfigured: missing 'api_key' credential"
  }
}

The provider connection the router picked has no usable credentials (the IAM variant
reads missing IAM credentials). This is a configuration problem on your institution’s
side. Contact your administrator.

Request rejected by the provider

{
  "error": {
    "message": "The provided model identifier is invalid."
  }
}

The provider rejected the request with a 400 or 422, and the router forwards its
message. The wording depends on the provider. A model id the catalog can’t resolve is
the most common cause: see Who validates the model.

401 Unauthorized

Missing credentials

{
  "error": {
    "message": "Authentication required"
  }
}

No Authorization header was sent. If the body is also invalid, you get the 422
first.

Wrong authorization scheme

{
  "error": {
    "message": "Authorization header must use the Bearer scheme"
  }
}

The header must be Bearer followed by a space and your key, as in
Authorization: Bearer sk-quantized-.... The scheme is case-sensitive.

Invalid API key

{
  "error": {
    "message": "Invalid API key"
  }
}

One message covers every credential failure:

  • the key or JWT doesn’t exist or doesn’t verify;
  • the key has been deactivated or has passed its exp;
  • the license or the license type has been deactivated.

See Authentication.

403 Forbidden

Service not enabled

{
  "error": {
    "message": "service 'embeddings' is not enabled for this license",
    "code": "policy_denied",
    "param": null
  }
}

Your license type’s policy doesn’t enable this endpoint. param is null. The service
names are chat_completions, responses, embeddings, gemini_embeddings,
bedrock_embeddings, image_generation, web_search, and fetch.

Parameter not allowed

{
  "error": {
    "message": "'temperature' must be between 0 and 1",
    "code": "policy_denied",
    "param": "temperature"
  }
}

The policy constrains a request field, and your value is outside what it allows.
param names the field. max_tokens also covers max_completion_tokens, and
reasoning_effort also covers reasoning.effort. The message depends on the kind of
rule:

Rule Message
Field not allowed parameter 'logprobs' is not permitted for this license
Fixed value 'model' must be 'openai/gpt-4.1-mini'
Allowed values 'model' must be one of: ['openai/gpt-4.1-mini', 'openai/gpt-4.1-nano']
Range 'temperature' must be between 0 and 1, 'max_tokens' must be <= 1000, 'n' must be >= 1
Excluded value 'model' value 'openai/o3' is not permitted for this license
Maximum length 'user' exceeds maximum length of 64
Maximum items 'tools' exceeds maximum item count of 4
Pattern 'model' value 'x-ai/grok-4' does not match any allowed pattern

Change the value, or ask your institution to change the policy. Retrying the same request
returns the same 403.

404 Not Found

Model or resource not found at the provider

{
  "error": {
    "message": "The model `nonexistent-model` does not exist or you do not have access to it."
  }
}

The provider reported that the model (or another resource) doesn’t exist. The router
forwards the provider’s message, so the wording varies, and an unknown model isn’t always
a 404. See Who validates the model.

405 Method Not Allowed

{
  "detail": "Method Not Allowed"
}

The path exists but not with this method, for example GET /v1/chat/completions.
Provider-backed endpoints are POST; /v1/models, /v1/license, and /v1/health are
GET.

422 Unprocessable Entity

The JSON body failed schema validation. detail lists every problem found, each with a
type, the loc path, a human-readable msg, and the input that was rejected. The
type values below are the ones you will usually see.

Missing field (missing)

{
  "detail": [
    {
      "type": "missing",
      "loc": ["body", "model"],
      "msg": "Field required",
      "input": {"messages": [{"role": "user", "content": "hi"}]}
    }
  ]
}

Unknown field (extra_forbidden)

{
  "detail": [
    {
      "type": "extra_forbidden",
      "loc": ["body", "top_k"],
      "msg": "Extra inputs are not permitted",
      "input": 40
    }
  ]
}

Every request body is strict: a field that isn’t documented for the endpoint is
rejected, not ignored. This catches typos and parameters from other APIs.

Value out of range (less_than_equal, greater_than_equal, …)

{
  "detail": [
    {
      "type": "less_than_equal",
      "loc": ["body", "temperature"],
      "msg": "Input should be less than or equal to 2",
      "input": 5,
      "ctx": {"le": 2.0}
    }
  ]
}

Empty string (string_too_short)

{
  "detail": [
    {
      "type": "string_too_short",
      "loc": ["body", "query"],
      "msg": "String should have at least 1 character",
      "input": "",
      "ctx": {"min_length": 1}
    }
  ]
}

Invalid JSON (json_invalid)

{
  "detail": [
    {
      "type": "json_invalid",
      "loc": ["body", 9],
      "msg": "JSON decode error",
      "input": {},
      "ctx": {"error": "Expecting value"}
    }
  ]
}

The body isn’t valid JSON. loc gives the character offset where parsing failed.

Cross-field rule (value_error)

{
  "detail": [
    {
      "type": "value_error",
      "loc": ["body"],
      "msg": "Value error, reasoning and reasoning_effort are mutually exclusive",
      "input": {"model": "openai/o4-mini", "messages": [], "reasoning": {"effort": "low"}, "reasoning_effort": "low"},
      "ctx": {"error": {}}
    }
  ]
}

A rule that spans several fields failed. The reason follows Value error, in msg.

Unrecognized Bedrock model family (union_tag_not_found)

{
  "detail": [
    {
      "type": "union_tag_not_found",
      "loc": ["body"],
      "msg": "Unable to extract tag using discriminator _request_discriminator()",
      "input": {"model": "foo.bar", "input_text": "hi"},
      "ctx": {"discriminator": "_request_discriminator()"}
    }
  ]
}

On /v1/aws-bedrock/embeddings, the body shape is chosen by the model prefix
(amazon.titan- or cohere.). Any other prefix can’t be matched to a shape.

500 Internal Server Error

Budget configuration error

{
  "error": {
    "message": "Budget configuration error"
  }
}

Your license’s budget configuration can’t be applied (for example an unknown strategy or
period, or a change of budget unit). Retrying won’t help. Contact your institution.

Unexpected provider error

{
  "error": {
    "message": "An error occurred"
  }
}

The provider answered with a status the router doesn’t map (anything other than the
cases under 400, 404, and 503). The message may be replaced by the provider’s own.
Retry once; report it if it persists.

Internal error

Internal Server Error

An unexpected failure inside the router. The body is plain text, not JSON. Report it with
the time of the request.

503 Service Unavailable

Provider unavailable

{
  "error": {
    "message": "Service temporarily unavailable"
  }
}

The provider timed out, couldn’t be reached, rate-limited the router, returned a 5xx,
or rejected Quantized’s own provider credentials. The message is always the same, so no
infrastructure details leak, and no Retry-After header is forwarded. Retry with
exponential backoff.

The same body is returned when your license’s policy can’t be evaluated (a policy saved
in a format the router doesn’t support). That failure doesn’t go away on retry. If 503
persists on every request, contact your institution.

Health check failing

{
  "status": "error"
}

Only from GET /v1/health, when the router can’t reach its database.

Errors during a stream

With "stream": true, every check up to the provider call happens before the stream
opens, so those errors come back as the normal JSON responses above. Once the stream has
started, the status is already 200, and a failure is reported inside the stream
instead.

On /v1/chat/completions, an error event is followed by the usual terminator:

data: {"error": {"message": "Stream error", "type": "server_error"}}

data: [DONE]

On /v1/responses, an error event ends the stream, with no [DONE]:

event: error
data: {"message": "Stream error"}

Treat either as a failed request: discard the partial output, or keep it and show the
response as incomplete. See Streaming.