Errors
An error response has a JSON body. Match on error.code, not on the message. The status tells you what kind of fix to make.
What to do
| Status | What to do |
|---|---|
| 400 | Change the request. No offer can serve it as it is. |
| 401 | Send a valid key. See API keys. |
| 402 | Add funds, raise the limit, or lower the token limit. |
| 403 | Use a key with the right scope, or change the request. |
| 404 | Check the name. GET /v1/models lists your models. |
| 409 | Use a new idempotency-key for a new body. |
| 413 | Send less data. See Modalities. |
| 429 | Wait for Retry-After, then send the request again. |
| 503 | Send it again later. Some codes need a change; the tables say which. |
| 500 | Send it again. If it repeats, give the job id in the message to support. |
When the auction refuses a request, eligibility.excluded lists each offer that it left out and why. See Exclusion reasons.
Error format
{
"error": {
"message": "`reasoning.effort` is not supported by any offer for kimi-k3",
"type": "liquid_error",
"code": "unsupported_parameter",
"param": "reasoning.effort",
"eligibility": { "excluded": [ … ] }
}
}
| Field | Description |
|---|---|
code |
Machine-readable error code. Match on this. |
message |
Human-readable description. |
param |
The request field that the error names, if any. |
eligibility |
Present when the auction refused the request. Lists each excluded offer and the reason. |
auto |
On no_eligible_offer for an Auto request: what Auto ranked by. See Auto Router. |
binding_rule |
On no_eligible_offer when a provider allowlist left no offer. |
fixes |
On no_eligible_offer: what to change. See Fixes. |
provider |
On provider_failed, request_rejected and tools_unsupported: the awarded provider. |
A 502 or 504 with an HTML body comes from the network in front of the exchange, not from the exchange.
POST /v1/messages returns the Anthropic format, {"type": "error", "error": {"type": ..., "message": ...}}. An auction error keeps the code, param, eligibility, auto, binding_rule and provider fields next to type and message.
| HTTP status | Anthropic error.type |
|---|---|
| 400 | invalid_request_error |
| 401 | authentication_error |
| 403, 451 | permission_error |
| 404 | not_found_error |
| 413 | request_too_large |
| 429 | rate_limit_error |
| 502, 503 | overloaded_error |
| other | api_error |
Error codes
Authentication
| Status | Code | Description |
|---|---|---|
| 401 | invalid_api_key |
Missing, unknown, revoked or expired API key. |
| 403 | insufficient_permission |
The key's scope does not cover the endpoint. |
| 403 | address_not_allowed |
The key is restricted to other source addresses. |
| 403 | impersonated_session |
An operator views the account, and the request would change it. |
| 451 | jurisdiction_restricted |
The request came from an unavailable location, or through Tor. See Regional availability. |
| 503 | identity_provider_unavailable |
The exchange could not verify a browser session. |
| 401 | unauthenticated |
Provider endpoints: missing or invalid provider key. |
Request
| Status | Code | Description |
|---|---|---|
| 400 | invalid_request |
The body cannot be parsed, or a field is not valid. param names the field. A removed exchange field gets this code. See Purchasing rules. |
| 400 | conflicting_parameters |
An exchange rule is set in the flat form and in a group with different values. The message names both fields. See Purchasing rules. |
| 400 | tools_unsupported |
The awarded provider cannot run the request with tools. Remove tools. |
| 400 | request_rejected |
The awarded provider refused the request. The message is its reason. |
| 400 | invalid_header |
A purchasing header cannot be parsed. |
| 400 | unsupported_parameter |
No offer for the model supports a parameter. param names it. See below. |
| 400 | unsupported_content |
No offer can serve a content part, or /v1/responses does not read an input item. The message gives its path, such as messages[0].content[1]. |
| 400 | unsupported_model |
An image or speech request named a router model that ranks by price. Name the model, or use a router model that ranks by Auto. |
| 400 | unsupported_rule |
An image or speech request had a rule that limits a price or a speed in tokens, such as exchange.rank.cost_tier. param names it. |
| 413 | request_too_large |
The body is over 20 MB (the default limit). See Modalities. |
| 404 | model_not_found |
The model is not a router model, and no offer quotes it. |
| 409 | idempotency_conflict |
The idempotency-key was reused with a different body. |
| 409 | idempotency_no_result |
The exchange saw the idempotency-key before, but the first request produced no receipt. |
| 429 | rate_limited |
Too many requests from the address, key or workspace, or a guardrail rate limit refused the call. Retry-After gives the seconds to wait. |
| 503 | overloaded |
The exchange is at its limit of work, or its database is too busy. It charged nothing. Retry-After gives 1 to 5 seconds. |
| 400 | invalid_end_user |
The end-user id is empty, over 512 characters or not printable ASCII. |
unsupported_parameter also applies to these cases:
- A control that no offer declared.
paramis the feature name:image.seed,image.background,image.output_compression,image.stream,video.seed,video.creativity,video.upscale_factor,video.continue,speech.instructions,speech.turns,transcription.diarize,transcription.keytermsorembedding.input_type. The message reads "image.seedis not supported by any offer for ". The exchange holds nothing. - A reference input that no offer reads.
paramisinput.image,input.audio,input.videoorinput.file. - An output option that no offer serves on an image, speech, video or embedding request.
paramisn,size,output_format,voice,response_format,secondsordimensions./v1/modelslistsimage_sizesfor an image model. - On
/v1/responses: a hosted tool other thanweb_search,previous_response_id,conversationandbackground: true. The exchange stores no responses. - On
/v1/messages: a hosted tool such asweb_search,web_fetchorcode_execution. A tool that your client runs is allowed. - A
providersetting that the exchange cannot apply. See Move from OpenRouter.
Models and strategy
These codes come before the auction.
| Status | Code | Description |
|---|---|---|
| 400 | basket_requires_models |
architect.basket needs exchange.candidates.models.allow. Send the models, or [] for every listed model. |
| 400 | address_takes_no_basket |
The model is one model, and the request also sent exchange.candidates.models.allow. Remove one. |
| 400 | basket_names_router |
An entry in exchange.candidates.models.allow is a router model or architect.basket. Use exchange.rank.strategy to rank. |
| 400 | invalid_basket |
An entry in exchange.candidates.models.allow is blank. |
| 400 | strategy_conflicts_with_address |
The strategy kind does not match the model, such as auto with a model. Send architect.basket. |
| 400 | invalid_price_band |
exchange.rank.strategy.price_band_bps is above 10000. |
| 403 | key_policy_violation |
The request is outside the preset of the API key: no model matches, or the exchange.rank.workload_profile differs. |
Auction
The price cap is the most that a job can cost. The exchange sets it before the first token.
| Status | Code | Description |
|---|---|---|
| 400 | no_eligible_offer |
The request, or the rules of the key or workspace, excluded every offer. Do not retry. Change the request. |
| 503 | no_eligible_offer |
No offer met the rules, and a wait can change that. For example, an offer failed a recent job. See eligibility.excluded. |
| 403 | no_eligible_offer |
A provider allowlist of the key or of a guardrail matches no provider that sells the model. binding_rule names it. Do not retry. |
| 503 | model_unavailable |
No seller can serve the named model now. param is model. Retry-After is 30. Retry later, or select another model from GET /v1/models. |
| 402 | insufficient_balance |
The cheapest eligible cap is more than the workspace balance. needed_usd and balance_usd give both amounts. |
| 402 | budget_exhausted |
The key reached its spending limit, or a guardrail budget is spent. Jobs in flight count at their price cap. |
| 402 | deposit_required |
The workspace holds only free credit, and keys of several such accounts send requests from this address. Add funds. |
| 403 | guardrail_violation |
The request is outside a workspace guardrail. See Guardrails. |
| 403 | content_blocked |
A guardrail blocks the request because its text matched a detector. See Guardrails. |
| 503 | guardrail_unavailable |
The router could not read the workspace guardrails, so it refused the call. Retry later. |
| 403 | pin_not_permitted |
A key that cannot pin offers sent x-liquid-pin-offer. |
| 503 | provider_failed |
The awarded provider failed, or the exchange could not reach it. If the provider refused the request, the code is tools_unsupported or request_rejected. |
| 404 | not_found |
The job does not exist or belongs to another workspace. |
If an unserved parameter is the only reason for every exclusion, the error is 400 unsupported_parameter.
When a provider fails
If the awarded provider fails before any output, the exchange releases the hold and tries the next offer.
- If no offer takes the job, you receive
provider_failed. The exchange keeps that offer out of the ranking for a while. - If no other seller has the model, the first request 30 seconds after the failure is a trial on that offer. Until then, and during the trial, requests get
model_unavailable. Each failed trial doubles the wait, up to 7.5 minutes.
After any output, or if the provider may still run the job, the exchange tries no other offer. You get one cap and one charge.
When an allowlist leaves no offer
An offer can serve the model while a provider allowlist matches none of the providers. The no_eligible_offer error then has error.binding_rule.
messagenames the providers and the model. Forallowed_providers, it also suggestsexchange.candidates.providers.prefer.- The exchange ranks the request again without the allowlist. If an offer can then win,
messagenames its providers and the lowest estimate for the job, andfixeshas the same data inadmitsandestimate_nanos. - The status is
400forallowed_providersand403for the allowlist of a key or a guardrail.retryableisfalse. - If the quotes of an allowed provider expired, the status is
503.
binding_rule |
The allowlist |
|---|---|
allowed_providers |
exchange.candidates.providers.allow in the request, or in the preset it names. |
key_allowed_providers |
exchange.candidates.providers.allow in the preset of the API key. Use another key. |
guardrail_allowed_providers |
exchange.candidates.providers.allow in a workspace guardrail. Change it in Guardrails. |
Auto
These codes refuse a request that Auto ranks. See Auto Router.
| Status | Code | Description |
|---|---|---|
| 503 | auto_unavailable |
Auto is off on this exchange, or it has no score snapshot yet. |
| 400 | auto_version_unknown |
The mode version is not defined. Send 1, 2, 3, 4 or 5. |
| 400 | auto_requires_price_bound |
No price bound. Send exchange.rank.cost_tier, exchange.limits.price.output_usd_per_mtok, exchange.cap_usd or x-liquid-cap-usd. |
| 400 | auto_preference_unsupported |
The mode version does not accept the preference. Version 1 accepts only premium; version 2 accepts all three. |
| 400 | auto_policy_unsupported |
The same, for a preference sent as exchange.policy or x-liquid-policy. |
| 400 | auto_pin_unsupported |
The request sent x-liquid-pin-offer. |
| 400 | preferred_providers_with_auto |
The request sent exchange.candidates.providers.prefer or provider.order. Auto ranks by score. |
| 400 | auto_reasoning_unsupported |
Auto has no scores for the requested reasoning effort. |
| 503 | auto_scores_stale |
The exchange's copy of the scores is older than its limit. |
A no_eligible_offer error for an Auto request also has error.auto: the decision that Auto made before the rules left no offer.
Ranking axis
A model-name suffix (smartest, fastest or cheapest) or a smartest strategy can return these codes. See Axis routing. smartest is Auto mode version 4, so it can also return the Auto codes.
| Status | Code | Description |
|---|---|---|
| 400 | smartest_needs_several_models |
smartest needs more than one model. Name a creator, a router model, a basket or a preset, or use fastest. |
| 400 | axis_conflict |
The model name and exchange.rank.strategy, x-liquid-policy or exchange.candidates.creators.allow ask for different rankings. |
| 400 | ambiguous_scope |
The name before the axis is both a served model and a creator. Send architect.basket with a pattern, such as deepseek-*. |
Provider endpoints
A buyer key does not reach these seller endpoints.
| Status | Code | Description |
|---|---|---|
| 400 | invalid_registration |
deployment_name, precision and region are required. |
| 400 | invalid_bands |
max_input_tokens and, if stated, max_output_tokens must be positive. |
| 404 | no_model |
The registration does not exist or belongs to another provider. |
| 409 | not_listed |
The exchange has not listed the registration. |
Reservations
You get these codes when a preset names a reservation. See Reservations.
| Status | Code | Description |
|---|---|---|
| 404 | reservation_not_found |
This workspace does not have the reservation contract that the preset names. |
| 409 | reservation_not_serving |
The reservation does not serve this month, and the preset names no other model. The term has not started, no deployment is linked, or the month is suspended. |
| 429 | reservation_pool_used |
This month's pool is used, and the preset names no other model. Add overflow models, or wait for next month. |
| 429 | reservation_rate_limited |
You passed the contract's tokens or requests a minute, and the preset names no other model. Retry after Retry-After, or add overflow models. The x-ratelimit-* headers show the limit. |
| 400 | reservation_needs_price_ranking |
The preset ranks a reservation by Auto, which has no score for it. Rank by price or by speed. |
Exchange
| Status | Code | Description |
|---|---|---|
| 503 | storage_unavailable |
A required exchange service is unavailable. The exchange did not process the request. Send it again. |
| 500 | internal_error |
Unexpected failure. If the request has a job, the message includes the job id. |
Fixes
Each entry in fixes is one change. reason is the exclusion reason, offers is the count of offers it left out, and message says what to change. The message of the error holds the same text, because many clients show only that field.
The exchange tests each change before it answers. It drops the one rule and ranks the request again. admits lists the providers that can then win. estimate_nanos is the lowest charge, in nano-USD (one USD is 1,000,000,000), that you can expect for this job at one of them. The charge at settlement follows the tokens that the job uses.
The message says |
What the test found |
|---|---|
Any one of these works |
Each entry is enough alone, and has admits. |
no single change |
The exchange tested each entry, and none is enough alone. Make more than one. |
Change one of these |
The exchange did not test each entry. An entry may not be enough alone. |
owner says who can make the change. A field of the request cannot widen a rule of a key preset or a guardrail.
owner.kind |
Who changes it | param and header |
|---|---|---|
request |
The sender of the request. | Name the field to set, when one field fixes the reason. |
key_preset |
An administrator of the workspace, in the preset that owner.name names. |
Absent. |
guardrail |
An administrator of the workspace, in the guardrail that owner.name names. owner.on_key is true when the guardrail is linked to this key. |
Absent. |
owner is absent when the state of the market caused the reason.
Exclusion reasons
Each entry in eligibility.excluded has provider, offer_id, reason and sometimes detail. The tables group the reasons by what you change.
Change the model or the provider
job_kind_not_served: the offer makes a different output, such as an image offer for a chat job.model_not_served: the offer does not serve the requested model.model_not_allowed: excluded byexchange.candidates.models.exclude, or outside the preset of the API key.provider_not_allowed: excluded byexchange.candidates.providers.allow.vendor_not_allowed: excluded byexchange.candidates.creators.allow.provider_blocked: excluded byexchange.candidates.providers.exclude.pinned_offer_not_resting: the pinned offer is not on the book.
Change a price limit
cap_exceeds_buyer_cap: the worst-case charge is more thancap_usd. Raisecap_usd, or lowermax_tokens.cap_exceeds_budget: the worst-case charge is more than the balance or the key limit. Add funds, raise the limit, or lowermax_tokens.rate_above_ceiling: a rate is more thanexchange.limits.price.input_usd_per_mtok,exchange.limits.price.output_usd_per_mtok,exchange.limits.price.cache_read_usd_per_mtokorexchange.limits.price.cache_write_usd_per_mtok.above_cost_tier: the price is above the requestedexchange.rank.cost_tier.
Change a speed limit
service_too_slow: the deadlines of the offer are longer thanexchange.limits.ttft_msorexchange.limits.completion_ms.throughput_too_low: the offer gives fewer tokens each second thanexchange.limits.min_tps.
Change the request itself
feature_unsupported: the offer does not support a parameter of the request.detailnames it.media_unsupported: the offer does not accept the attachment in the form sent.detailnames the constraint, such asimage.max_bytes.context_too_large: the input andmax_tokenstogether exceed the context window of the offer. See Output limits.output_bound_unsupported:max_tokensis outside the output limit of the offer. On a text job,detailgives the request and the range, such asasked 200000, accepts 1 to 8192. On an image, speech, video or embedding job,detailnames the parameter that the deployment does not serve. See Output limits.
Change a data rule
region_policy: excluded byexchange.privacy.regions.retention_policy: the offer cannot meet the requestedexchange.privacy.retention.workload_profile_not_targeted: the offer does not target the requestedexchange.rank.workload_profile.
Nothing to change
offer_expired: the offer expired before the auction ran. Send the request again.superseded_by_own_offer: the same provider had an eligible offer that ranked ahead of this one.
Auto has no score
score_unmapped: Auto has no score for the deployment at the reasoning setting of the request.score_missing: the deployment has no score for the task Auto ranked by.detailnames the score field.
Output limits
max_tokens is a maximum for the answer. If every offer is excluded, and output_bound_unsupported or context_too_large excluded at least one, the auction runs again. It tries a lower max_tokens for each of those offers, from the largest value to the smallest.
output_bound_unsupported: the lower value is the output limit of the offer.context_too_large: the lower value is the room that the input leaves in the context window, and not more than the output limit.- An offer gives no value if the room is less than 1,024 tokens, or if another reason excluded it.
- The auction keeps the first value that an offer accepts.
The exchange calculates the cap from the lower value. An answer that reaches it stops with finish_reason: "length" (stop_reason: "max_tokens" on the Anthropic endpoint).
A request with no limit (max_tokens, max_completion_tokens or max_output_tokens) starts at 32,768 tokens. If the cap or your balance excludes all offers, the exchange halves that limit until an offer fits, down to 1,024 tokens. It never changes a limit that you set.
- If your limit does not fit your balance, the response is
402 insufficient_balance.needed_usdis the lowest cap for your limit. - If your limit does not fit your cap, the response is
400 no_eligible_offerwithparamset toexchange.cap_usd. - The exchange never lowers
max_tokensto or below athinkingbudget. It refuses such a request withno_eligible_offer.
Reasoning models
The thinking of a reasoning model counts toward max_tokens, and the exchange bills it as output.
- On
POST /v1/chat/completions, the thinking arrives asreasoning, on each streameddeltaor on themessage. usage.completion_tokens_details.reasoning_tokensandexchange.reasoning_tokensgive the tokens that the thinking used.- A model that thinks until the limit stops with
finish_reason: "length"and an emptycontent. You pay for the thinking. Use a largermax_tokens. - A model that stops after its thinking with no text and no tool call gives you nothing to use. The job fails, you pay nothing, and the receipt records
empty_response. - On
POST /v1/messages, the response has thinking blocks only ifthinkingis notdisabled. Without it, a smallmax_tokenscan end with no text.
Tool calls
If the request sends tools, the exchange checks each tool call in the answer. A call is bad when it has one of these faults:
| Fault | Meaning |
|---|---|
invalid_json |
The arguments are not JSON. |
unknown_name |
The name is not one of the tools in the request. |
schema_mismatch |
The arguments break the schema of the tool. |
The receipt has the counts in answer_checks.tool_calls, answer_checks.tool_call_errors and answer_checks.tool_call_faults.
- Not streamed. The exchange reads the whole answer first. If a call is bad, it drops the answer and sends the job to the next offer. If no offer is left, the job fails with
invalid_tool_call. - Streamed. The exchange counts the bad calls and keeps the answer.
Tool-call markup. Some providers do not turn the calls of a model into tool_calls, and your agent loop stops. The exchange sets answer_checks.tool_markup to 1 when the request sent tools, the answer has no tool call, and the text holds DeepSeek, Hermes, Qwen or Kimi markup. It keeps the answer and records the markup for the deployment.
Structured output
If you send response_format with json_object or json_schema, the exchange checks the answer when the job ends. json_object asks for one JSON value. json_schema also asks that the value matches your schema. On POST /v1/messages it checks output_config.format, and on POST /v1/responses it checks text.format.
The result is in exchange.answer_checks.structured_output, on the last chunk of a stream.
result |
Meaning |
|---|---|
passed |
The answer is JSON, and for json_schema it matches the schema. |
not_json |
The answer is not one JSON value. Text around the value, or a code fence, gives this result. |
schema_mismatch |
The answer is JSON but breaks the schema. keyword names the JSON Schema keyword, such as required. |
cut |
The answer stopped at max_tokens. Send the request again with a larger max_tokens. |
refused |
The answer has no text, or the model refused. |
unchecked |
The exchange did not check the answer: only tool calls, or the schema or answer is too large or invalid. |
- Not streamed. For
not_jsonorschema_mismatch, the exchange drops the answer and sends the job to the next offer. If no offer is left, the job fails with no charge, and the receipt recordsinvalid_structured_output. - Streamed. You receive the answer as the provider sent it, and you pay for it.
Only not_json and schema_mismatch count against the provider.
If the answer writes more than 1,024 whitespace characters in a row outside a JSON string, the exchange stops it and counts it against the provider. A request that is not streamed goes to the next offer. A stream fails with provider_failed, and you pay nothing.
The receipt keeps the result and the keyword, not the path of the failed field.