Error Codes
Every Router error uses the same OpenAI-compatible shape. error.type puts the failure in a bucket, error.code says exactly what happened, and request_id identifies the request — quote it in any support request.
{ "error": { "message": "Insufficient balance to process request", "type": "payment_error", "code": "insufficient_balance" }, "request_id": "<REQUEST_ID>"}Branch on error.code, not on error.message. Messages are human-readable and may be reworded; codes are the contract.
HTTP status codes
Section titled “HTTP status codes”| Status | Meaning |
|---|---|
400 |
Bad request — malformed body, unsupported capability, or a bad routing header |
401 |
Missing or invalid authentication |
402 |
Insufficient balance |
403 |
The credential is not permitted to perform this action |
404 |
Resource not found |
429 |
Rate limited — read Retry-After |
502 |
The provider returned an error and failover was exhausted |
503 |
No healthy provider available for the request |
Types and codes
Section titled “Types and codes”type |
code |
Status | When it happens |
|---|---|---|---|
invalid_request_error |
invalid_body |
400 | The request body is malformed or violates the schema |
invalid_request_error |
invalid_provider_header |
400 | An X-0G-Provider-* header has a value outside its domain, such as Sort: fastest or Allow-Fallbacks: 1 |
invalid_request_error |
invalid_trust_mode |
400 | X-0G-Provider-Trust-Mode is not standard, verified, or private |
invalid_request_error |
invalid_max_price_usd |
400 | A Max-Price-Usd-* value is not a finite, non-negative decimal |
invalid_request_error |
no_provider_within_max_price |
400 | The price ceiling filtered out every candidate |
invalid_request_error |
pinned_provider_exceeds_max_price |
400 | The pinned provider address is priced above the ceiling |
invalid_request_error |
model_not_capable |
400 | Require-Parameters: true and no provider supports the requested parameters |
invalid_request_error |
no_provider_within_max_tokens |
400 | Require-Parameters: true and no provider advertises enough output capacity |
invalid_request_error |
missing_authorization |
401 | No Authorization header |
invalid_request_error |
invalid_api_key |
401 | The key does not exist or is malformed |
permission_error |
trust_mode_mismatch |
403 | A request header asked for a different trust mode than the key enforces |
payment_error |
insufficient_balance |
402 | Not enough 0G deposited to cover the request |
permission_error |
access_denied |
403 | The credential is not allowed to perform this action |
permission_error |
insufficient_scope |
403 | The credential lacks the required scope, most often a sk- key calling /v1/account/* |
not_found_error |
api_key_not_found |
404 | The referenced key does not exist |
rate_limit_error |
rate_limit_exceeded |
429 | Over the request limit; read Retry-After |
server_error |
provider_error |
502 | The provider failed and every healthy candidate had been tried |
server_error |
no_available_provider |
503 | No healthy provider serves this model right now |
server_error |
no_provider_for_trust_mode |
503 | No provider in the requested trust tier is available |
Every routing code in this table comes from one of the eight X-0G-Provider-* headers. See Provider Routing Headers for the semantics behind each one.
The two 503s are different problems
Section titled “The two 503s are different problems”no_available_provider means the model has no healthy capacity at all. no_provider_for_trust_mode means capacity exists but not in the tier you asked for; the message names the tier, as in no provider available for trust mode: tier=private. The Router returns this rather than silently serving you from a weaker tier. Either wait, or move to a model with providers in that tier. See Trust Modes.
Why an empty price-filtered pool is a 400
Section titled “Why an empty price-filtered pool is a 400”no_provider_within_max_price looks like a capacity problem but is not. The pool is empty because of a constraint you set, and no amount of waiting changes that: it is structural, not transient. It is a 400 so your retry logic treats it as “fix the request” rather than “try again in a moment.” Same reasoning for pinned_provider_exceeds_max_price.
Retryability
Section titled “Retryability”| Status | Retry? | How |
|---|---|---|
429 |
Yes | Honor Retry-After, in seconds. Never retry in a tight loop — read X-RateLimit-Remaining-Requests and slow down before you get here. |
502 |
Yes, briefly | Failover already tried every healthy provider, but one may have just come back. Retry with backoff and give up after a small number of attempts. |
503 |
Not immediately | Unlikely to resolve in seconds. Switch models or wait; do not hammer. |
400 |
No | The request is wrong. Retrying it unchanged produces the same error. |
401 |
No | Fix the credential. |
402 |
No | Add funds; retrying cannot create balance. See Get 0G and fund your account. |
403 |
No | Use a credential with the right scope. |
The rule of thumb: 4xx means change something before you retry, 5xx means the request was fine and the network was not. The exception that catches people is no_provider_within_max_price, a 4xx that reads like a capacity error. It is still a 4xx: raise the ceiling or drop it.
Common cases
Section titled “Common cases”403 insufficient_scope on an account endpoint. You are using a sk- key. Inference keys can no longer read /v1/account/*; that requires an mk- key with account:read. See API & Authentication.
400 invalid_provider_header on a header you believe is fine. Check case and type. Sort must be exactly latency or price, lowercase. The boolean headers accept only true or false, case-insensitively, so 1 and yes are rejected.
401 invalid_api_key from a deployment that was working. A revoked key returns the same code as an unknown one. Revocation is immediate, with no grace period, so roll the new key everywhere before revoking the old.
A 400 on a header you meant to leave unset. Absent and blank headers are always treated as unset and never error, so this means something in your stack is emitting a real, malformed value.
402 insufficient_balance mid-job. Balance is drawn down per request. Top up before long batch runs — see Get 0G and fund your account.
Next steps
Section titled “Next steps”- Provider Routing Headers — the header semantics behind the
400s - Model Catalog — picking a model with supply in the tier you need
- API & Authentication — which credential each endpoint accepts