Common errors
When a call fails, check the HTTP status and error code, then use the tables below. Error messages follow the Accept-Language header (zh-CN or en-US).
Error formats
| API | Format |
|---|---|
OpenAI-compatible /v1/* |
{"error": {"message", "type", "code", "request_id"}} |
| Anthropic-compatible | {"type": "error", "error": {"type", "message"}}, with no machine-readable code |
| Gemini-compatible | {"error": {"code", "message", "status"}}, where code is the HTTP status; there is no platform error code |
Platform API /api/v1/*, media tasks |
{"code", "message", "request_id", "details"} |
Include the request_id when contacting support. Once a call has gone to an upstream, the X-ModelPortal-Request-Id response header carries its request ID, which you can look up in the console's call details.
Error codes
| Code | HTTP | Cause | What to do |
|---|---|---|---|
invalid_request |
400 | The body or a parameter is invalid, including unknown fields and nonexistent provider or endpoint identifiers in provider; the message names the field |
Fix the request as the message says |
unsupported_parameter |
400 | No candidate endpoint supports a parameter or capability in the request, including turning reasoning off on models that cannot; the message lists the parameters | Drop the parameter or use another model |
stateful_not_supported |
400 | /v1/responses carried previous_response_id, conversation or background; stateful conversations are not supported yet |
Keep the context yourself and send the full input each time |
invalid_grant |
400 | The app authorization code is invalid, expired or used, or the key can no longer be created | Start the authorization again |
invalid_api_key |
401 | Missing, wrong or revoked key, or a key put in the URL of the OpenAI or Anthropic API | Check the key and the request header |
key_inactive |
401 | The key is paused or expired, or its app is disabled | Resume the key or create a new one |
insufficient_balance |
402 | Balance cannot cover the call's estimated cost, or the account is in arrears | Top up |
budget_exceeded |
402 | The key hit its daily or monthly spend limit | Raise the limit or wait for the period to roll over |
key_restricted |
403 | The model is not in the key's allowed models, the source IP is not on the key's allowlist, or this kind of key cannot use the endpoint | Update the key's allowed models or IP allowlist |
request_blocked |
403 | The request was blocked by risk control, or the account is frozen, API-paused or banned. Content that hits a content rule, and requests with images while image moderation is unavailable, also get this code; the message ends with the category, such as content.prohibited.violence or content.moderation_unavailable |
Check notifications or open a ticket; for blocked content, change it or appeal |
region_restricted |
403 | The request comes from a region that is not supported | The service is not available there; open a ticket if this looks wrong |
verification_required |
403 | Identity verification is needed first | Complete verification in the console |
model_not_found |
404 | Unknown, in maintenance, or lacking the capability this endpoint needs | Check the model ID and capabilities |
model_retired |
410 | The model is retired; the message names the replacement when one is set | Switch to the replacement model |
no_eligible_endpoint |
422 | No endpoint meets the constraints; the body carries an excluded list |
Adjust constraints using the exclusion reasons below, or retry later |
rate_limited |
429 | The account or key hit its requests-per-minute or concurrent-request limit; carries Retry-After |
Back off and retry, or wait for running requests to finish |
capacity_exhausted |
429 | Every candidate endpoint is at capacity (at its declared limit, or in the short cooldown after an upstream 429), or every attempt got an upstream 429; carries Retry-After |
Retry after Retry-After |
upstream_failed |
502 | Every permitted attempt failed, and not all of them with an upstream 429; the body carries attempts |
Retry later; failed calls are free |
These codes are outside the standard set above but can also appear:
| Code | HTTP | Cause | What to do |
|---|---|---|---|
request.too_large |
413 | The request body exceeds the size limit | Shrink the body; upload large files first |
request.endpoint_moved |
410 | A retired address was used, such as anything under /anthropic/* |
Use the new address given in the message |
upstream.unavailable |
503 | A service the platform depends on (such as rate limiting) is unavailable | Retry later |
Failed attempts
upstream_failed, and capacity_exhausted caused by upstream 429s, carry attempts in the error object, one item per attempt in order:
{"attempts": [{"provider": "PROVIDER_ID", "endpoint": "ENDPOINT_ID", "status": 503, "cost": 0.000000}]}
provider is the provider identifier, endpoint the endpoint identifier, status the upstream HTTP status (0 when the connection failed or timed out), and cost is 0 for failed attempts. When every attempt got an upstream 429 the call returns 429 capacity_exhausted, and Retry-After is the earliest time any attempt recovers: the upstream's Retry-After when it sent one, otherwise the end of the short cooldown.
Exclusion reasons for 422
The body of no_eligible_endpoint carries an excluded list naming each endpoint that passed your constraints but was excluded, with a reason code:
{"excluded": [{"offering_id": "route_xxxxxxxx", "reason": "unavailable"}]}
| Reason code | Meaning |
|---|---|
unavailable |
The provider is not live, the access point is not ready, or the endpoint is neither Available nor Degraded (for example paused or in maintenance) |
region |
The endpoint does not serve the request's source region |
capability |
The endpoint lacks a capability or parameter the request uses |
context |
The estimated input (plus any output limit you set) exceeds the endpoint's context length |
circuit_open |
The endpoint's circuit breaker is open or half-open |
upstream_cooldown |
The endpoint is in a short cooldown after an upstream 429 |
capacity |
The endpoint reached its declared RPM or TPM in the current 60-second window |
session |
The request carries a signed thinking block that only the signing endpoint can serve, and it is unavailable |
If your own constraints (only, ignore, data_collection, quantizations, max_price, data_regions, exclude_regions) exclude every endpoint up front, excluded is an empty array and the message names the constraints. Endpoints that were never shown publicly are not listed.
Two exceptions: when parameters or capabilities alone leave no endpoint, you get 400 unsupported_parameter; when capacity or an upstream 429 cooldown alone leaves none, you get 429 capacity_exhausted with Retry-After set to the earliest recovery.
Rate limits
Each account has three limits: requests per minute for the account, requests per minute for each key, and requests in progress at the same time. Defaults go up with the account's total top-ups:
| Total top-ups | Account requests per minute | Requests per minute per key | Requests in progress |
|---|---|---|---|
| From 0 | 600 | 600 | 5 |
| From 50 USD | 1,200 | 1,200 | 10 |
| From 500 USD | 3,000 | 3,000 | 30 |
Going over any of them returns 429 rate_limited, and the Retry-After header gives the suggested wait in seconds. Your current limits are on the API keys page of the console. Open a ticket if you need higher limits.
Retrying
- 429, 502 and 503 are retryable; use exponential backoff with a retry cap.
- 400, 401, 402, 403, 404, 410 and 422 will not succeed on retry; fix the cause first.
- Before the first content event, all attempts share a 600-second total timeout; once output starts, a gap of more than 120 seconds between events closes the stream. Use streaming for long outputs.
FAQ
- How should I retry a 429?
- rate_limited is retryable and covers both rate and concurrency limits. Use exponential backoff with a retry cap and reduce concurrent requests.
- Am I charged for upstream_failed?
- No. Calls that fail or time out upstream count as failed and are free; retry later.
- Why is there no error code in the Anthropic format?
- Anthropic error bodies carry only a type and message. Use the OpenAI-compatible API if you need machine-readable codes.