Errors and recovery reference
Understand errors in client responses, route inspection, and request logs, and decide whether to retry, recover, or change configuration.
Three distinct error namespaces §
One failure can expose three kinds of code, each with a different purpose:
| Field | Where it appears | Purpose |
|---|---|---|
| code | Client HTTP response | Tells the caller why this request failed |
| error_code | Request log and attempt chain | Normalizes upstream failures for filtering and diagnosis |
| reason_code | Route checking | Explains why an AccessKey, Group, or credential cannot be a candidate |
Route inspection may show no_available_group, while the real request may return no_available_candidate and the request log may record upstream_client_error for the last upstream attempt. Automation must read the field defined by the interface it is calling.
Management API message values are localized, and upstream messages may change. Programs should use code and structured data for decisions; message and error_summary are for human readers only.
Management API errors §
Management API error codes use uppercase snake case. See Management API for the response envelope, authentication, and main resources.
Common, authentication, and resource errors
| Error code | HTTP | Meaning | How to handle |
|---|---|---|---|
| BAD_REQUEST | 400 | Request parameters or path are invalid | Correct the request and try again |
| INVALID_JSON | 400 | The JSON format, fields, or body shape is invalid | Correct the JSON to match the endpoint contract |
| VALIDATION_FAILED | 400 | The request parsed but failed business validation | Inspect field-location details in data |
| REQUEST_TOO_LARGE | 413 | The management request body exceeds the size limit | Reduce the body or split the operation |
| UNAUTHORIZED | 401 | The management credential is invalid | Check the AUTH_KEY or AccessKey |
| FORBIDDEN | 403 | The current identity cannot perform this operation | Use AUTH_KEY or reduce the operation scope |
| AUTH_LOCKED | 429 | The direct peer was temporarily locked after repeated authentication failures | Wait for Retry-After and stop retrying the wrong key |
| NOT_FOUND | 404 | The target resource does not exist | Refresh the resource list and verify the ID |
| ROUTE_NOT_FOUND | 404 | The requested management route does not exist or has been retired | Check the path against the current version's route contract |
| METHOD_NOT_ALLOWED | 405 | The management route exists, but the HTTP method is unsupported | Use a method declared by that route |
| DUPLICATE_RESOURCE | 409 | The unique resource already exists | Reuse the existing resource or change the unique field |
| BAD_GATEWAY | 502 | An upstream request required by the management operation failed | Inspect data and upstream status before retrying |
| DATABASE_ERROR | 500 | The database operation failed | Check service logs and database availability |
| INTERNAL_SERVER_ERROR | 500 | An unclassified internal error occurred | Diagnose with service logs; do not blindly replay writes |
Idempotency, concurrency, and runtime recovery
| Error code | HTTP | Meaning | How to handle |
|---|---|---|---|
| IDEMPOTENCY_KEY_REQUIRED | 428 | This write operation requires an Idempotency-Key | Generate a canonical UUID v4 and send it with the request |
| INVALID_IDEMPOTENCY_KEY | 400 | The Idempotency-Key is not a canonical lowercase UUID v4 | Replace it with a canonical UUID v4 |
| IDEMPOTENCY_KEY_REUSED | 409 | The same idempotency key was used for a different request | Generate a new key for the new logical operation |
| IDEMPOTENCY_RESULT_EXPIRED | 410 | The idempotent result expired, but the operation identity remains known | Use data to verify the completed resource instead of creating it again |
| CONTROL_OPERATION_INCOMPLETE | 503 | The database commit succeeded, but runtime recovery is incomplete | Keep the same idempotency key and wait for automatic reconciliation |
| CONTROL_RECOVERY_PENDING | 503 | An earlier committed operation is still recovering | Wait for data.retry_after_ms before retrying |
| SETTINGS_PRECONDITION_REQUIRED | 428 | The settings update is missing If-Match | Read the settings and ETag, then update with If-Match |
| SETTINGS_VERSION_CONFLICT | 412 | The settings changed after they were read | Merge again using the current settings in data |
Groups, models, and subscription credentials
| Error code | HTTP | Meaning | How to handle |
|---|---|---|---|
| GROUP_IN_USE | 409 | The Group is still referenced by AccessKeys | Remove the references listed in data first |
| INVALID_CREDENTIAL_STATE | 409 | The credential cannot be restored from its current state | Refresh credential state and choose an allowed operation |
| CHANNEL_TARGET_CONFLICT | 409 | Another Group already uses the same channel target | Reuse the existing Group or explicitly confirm the duplicate target |
| MODEL_NAME_CONFLICT | 409 | Client-visible model names conflict within the Group | Correct model names or aliases using data.conflicts |
| NO_ACTIVE_CREDENTIAL | 409 | The Group has no credential available for the operation | Add, enable, or reauthorize a credential |
| MODEL_PRICE_UNPRICED_CONFIRMATION_REQUIRED | 409 | Marking the model as unpriced requires explicit confirmation | Confirm and submit again |
| MODEL_PRICE_REFERENCED | 409 | The model price is still referenced by Groups | Remove references before deleting the price |
| MODEL_PRICE_AUTOMATIC_DELETE_FORBIDDEN | 409 | Automatically synchronized model prices cannot be deleted manually | Change the sync source or wait for a later sync |
| OAUTH_FILE_INVALID | 400 | The OAuth JSON is unrecognized or contains invalid fields | Export again and import the complete file |
| OAUTH_FILE_TOO_LARGE | 413 | The OAuth file exceeds the size limit | Keep only the required credential content |
| AUTHORIZATION_UNAVAILABLE | 503 | Browser authorization, device-code authorization, or subscription credential refresh is temporarily unavailable | Check channel capabilities, networking, and service logs |
| AUTHORIZATION_STATE_INVALID | 400 | The authorization callback state is invalid or mismatched | Start a new authorization |
| AUTHORIZATION_EXCHANGE_FAILED | 502 | Exchanging the authorization code for a credential failed | Check upstream status and authorize again |
| STAGED_CREDENTIAL_NOT_READY | 409 | The staged credential has not completed authorization | Complete authorization before connecting it |
| STAGED_CREDENTIAL_EXPIRED | 410 | The staged credential expired | Import or authorize again |
| STAGED_CREDENTIAL_CONSUMED | 409 | The staged credential was already consumed | Refresh the list and do not connect it again |
| STAGED_CREDENTIAL_MISMATCH | 409 | The staged credential does not match the target Group | Select the matching channel and Group |
| DUPLICATE_CREDENTIAL_IDENTITY | 409 | The same subscription account already exists in the Group | Use the existing account or connect it to another Group |
| CREDENTIAL_REAUTHORIZATION_REQUIRED | 409 | The credential requires reauthorization | Complete OAuth authorization again |
| CREDENTIAL_AUTH_OUTCOME_UNKNOWN | 409 | The authorization outcome cannot be confirmed | Refresh state before starting another authorization |
| CREDENTIAL_REFRESH_TEMPORARILY_UNAVAILABLE | 503 | The credential cannot be refreshed temporarily | Retry later or use another credential |
| CREDENTIAL_VERSION_CONFLICT | 409 | The credential changed during the operation | Refresh the credential and repeat the operation |
| RESET_CREDIT_UNAVAILABLE | 409 | No reset credit is currently available | Wait for the upstream to provide another reset opportunity |
| RESET_CREDIT_REJECTED | 502 | The upstream rejected the quota reset | Inspect account status and contact the upstream |
| RESET_CREDIT_OUTCOME_UNKNOWN | 503 | The quota-reset outcome cannot be confirmed | Retry with the same Idempotency-Key |
data is returned only when callers need it to make a decision. Common contents include conflicting resources, field locations, current settings and ETag, operation IDs, failed stages, retry timing, and quota details. Errors without a declared shape usually omit data.
Structured data for management errors
The table lists only management errors with stable structured data. The same error code may still omit data in another context.
| Error code | data fields | Returned when |
|---|---|---|
| VALIDATION_FAILED | entry, field, reason_code | Some credential field validations fail |
| AUTH_LOCKED | retry_after_seconds | Authentication is locked; a Retry-After header is also returned |
| BAD_GATEWAY | trigger, checked_at_ms, successful_fetch_at_ms, not_modified, skipped, error_code | A manual Models.dev sync fails |
| IDEMPOTENCY_KEY_REUSED | operation_id, operation_kind | The idempotency key belongs to another request |
| IDEMPOTENCY_RESULT_EXPIRED | operation_id, operation_kind, resource_identity, completed_at_ms | The idempotent result has been compacted |
| CONTROL_OPERATION_INCOMPLETE | operation_id, operation_kind, last_completed_stage, failed_stage, can_reconcile | Runtime recovery remains incomplete after the database commit |
| CONTROL_RECOVERY_PENDING | operation_id, operation_kind, failed_stage, retry_after_ms | An earlier committed operation blocks the current write |
| SETTINGS_VERSION_CONFLICT | settings, settings_etag | The If-Match value is stale |
| GROUP_IN_USE | access_keys[] { id, name } | Deleting a Group still referenced by AccessKeys |
| CHANNEL_TARGET_CONFLICT | groups[] { id, name } | Creating a duplicate channel target without confirmation |
| MODEL_NAME_CONFLICT | conflicts[] { client_model, indexes } | Group model names or aliases conflict |
| MODEL_PRICE_UNPRICED_CONFIRMATION_REQUIRED | id | Setting every price to null without confirmation |
| MODEL_PRICE_REFERENCED | id, reference_count, reference_group_count | Deleting a price still referenced by Groups |
| MODEL_PRICE_AUTOMATIC_DELETE_FORBIDDEN | id | Deleting an automatically synchronized price |
Data-plane errors §
Data-plane errors generated by GPT-Load use lowercase snake case and the base shape { "code": "...", "message": "..." }. Cost-limit errors also return structured error and data.
| Error code | HTTP | Meaning | How to handle |
|---|---|---|---|
| invalid_access_key | 401 | The AccessKey is missing, disabled, expired, or not allowed from this source | Check the AccessKey used by the client; these failures are not written to request logs |
| protocol_endpoint_not_found | 404 | The path is not an enabled data-plane endpoint | Verify the base URL, protocol, and path |
| method_not_allowed | 405 | The endpoint exists, but the HTTP method is unsupported | Use a method declared by the endpoint |
| invalid_protocol_request | 400 | The request body or protocol fields cannot be parsed | Correct the request for the client protocol |
| model_required_by_filter | 400 | The AccessKey filters models, but the request has no model | Specify a model or remove the model filter |
| no_available_candidate | 503 | No Group or credential is currently routable | Use route inspection to find the specific reason_code |
| upstream_connect_failed | 502 | No available upstream could be reached | Check the network, proxy, and upstream address before retrying |
| upstream_timeout | 504 | The upstream request timed out | Inspect dispatch and commit state in the request log; do not blindly replay non-idempotent requests |
| upstream_protocol_error | 502 | The upstream response could not be processed safely | Check the upstream response format, Content-Encoding, and service logs |
| protocol_conversion_unsupported | 422 | No route can execute the request natively or convert it safely | Change the protocol, operation, or channel |
| request_too_large | 413 | The data-plane request body exceeds the size limit | Reduce the request body |
| unsupported_content_encoding | 415 | The request uses an unsupported Content-Encoding | Use identity, gzip, br, deflate, or zstd |
| invalid_content_encoding | 400 | The compressed request body cannot be decoded | Encode the body again and verify the request headers |
| not_acceptable | 406 | The client does not accept an identity-encoded response | Allow identity-encoded responses |
| model_list_too_large | 500 | The visible model list exceeds the safe response limit | Reduce the models visible to the AccessKey |
| access_key_rate_limited | 429 | The AccessKey exceeded its RPM limit | Wait for Retry-After |
| access_key_cost_limit_exceeded | 429 | The AccessKey reached an estimated-cost limit | Inspect data.recoverable, next_available_at_ms, and blocking_rules |
| configuration_changed | 503 | The configuration snapshot used by the request is stale | Wait briefly according to Retry-After, then retry |
For access_key_cost_limit_exceeded, error contains type, code, message, and optional resets_at in Unix seconds; data contains recoverable, next_available_at_ms in Unix milliseconds, and blocking_rules. Each blocking rule contains id, kind, limit_usd, and used_usd; periodic rules also include period_seconds and window_ends_at_ms.
Native routes return the upstream error body after redaction and safety checks; converted routes project it into the client protocol's error shape. OpenAI, Anthropic, and Gemini clients may therefore see different fields. Do not assume every failure contains only code and message.
Request-log errors §
Top-level error_code is the final request outcome; attempts[].error_code belongs to one upstream attempt. When a retry succeeds, the top level may have no error while earlier attempts retain their error codes.
This table lists only additional normalized request-log codes. Codes shared with fixed data-plane errors are defined in the previous section and are not repeated.
| Error code | Meaning | How to handle |
|---|---|---|
| upstream_rate_limited | The upstream rate-limited this attempt | Inspect Retry-After, cooldown, and later attempts |
| upstream_model_unavailable | The upstream model or candidate is unavailable | Check model configuration and later candidates |
| upstream_invalid_key | The upstream rejected the channel credential | Update the credential and check accumulated failures or blacklisting |
| upstream_authentication_required | The subscription credential needs refresh or reauthorization | Inspect the retry decision and reauthorize |
| upstream_host_error | The upstream returned a server error | Check whether the Group was skipped and whether another attempt ran |
| upstream_client_error | The upstream considers the request itself invalid | Inspect the error summary and request parameters; switching credentials is usually inappropriate |
| upstream_error | An upstream failure that could not be classified further | Use the status, summary, and matched rule together |
| upstream_sse_error | The upstream reported an error in an SSE event | Check whether the stream was committed; it cannot be retried after output starts |
| upstream_stream_terminated | The upstream stream ended before completion | Check the network and stream idle timeout |
| upstream_stream_idle_timeout | The upstream stream produced no data for too long | Adjust the stream idle timeout or inspect the upstream |
| upstream_response_incomplete | The upstream explicitly reported an incomplete response | Inspect the error summary and upstream request ID |
| downstream_write_failed | Writing the response to the client failed | Check client disconnects, the reverse proxy, and the network |
| client_canceled | The client canceled the request | Usually no action is required |
| server_shutdown | The request was canceled during server shutdown | After service recovery, the caller decides whether to retry |
| internal_error | The request ended without a classifiable normal result | Inspect service logs for the same Request ID |
| credential_decrypt_failed | The candidate credential could not be decrypted | Restore the matching ENCRYPTION_KEY or enter the credential again |
| credential_normalization_failed | The candidate credential could not be normalized for execution | Enter a valid credential again |
| credential_proxy_prepare_failed | The credential-level proxy could not be initialized | Correct the proxy configuration for this credential |
| group_proxy_prepare_failed | The Group-level proxy could not be initialized | Correct the Group proxy configuration |
| server_is_overloaded | The upstream explicitly reported overload | Inspect replay safety and later candidates |
| rate_limit_exceeded | The upstream explicitly reported capacity limiting | Inspect cooldown and later candidates |
error_summary is redacted and length-bounded; raw error bodies are never written to request logs. AccessKey authentication fails before a RequestLog exists, so it only returns invalid_access_key and emits a rate-limited security event.
Request-log fields §
Request level
| Field | Values | How to interpret it |
|---|---|---|
| request_id | UUID v4 | GPT-Load request ID used to correlate request details and service logs |
| status | success / error / incomplete / canceled | Final status of the whole request |
| status_code | 0 / HTTP status | Final request status code; it can be 0 when no HTTP response was produced |
| error_code | Normalized error code | Use it for filtering and aggregation; do not infer it from error_summary |
| error_summary | Redacted, length-bounded summary | For human diagnosis; it does not guarantee verbatim upstream text |
| attempt_count | Non-negative integer | Actual number of recorded upstream or local execution attempts |
Attempt fields in attempts[]
| Field | Values | How to interpret it |
|---|---|---|
| sequence | Integer starting at 1 | Attempt order within this request |
| status_code | 0 / HTTP status | Status code for this attempt; it can be 0 when the upstream did not respond |
| error_code | Normalized error code | Failure reason for this attempt; an empty string on success |
| error_summary | Redacted, length-bounded summary | For human troubleshooting only |
| failure_category | ok / rate_limited / model_unavailable / invalid_key / upstream_host_error / client_error / conversion_unsupported / downstream_cancel / authentication_required / ambiguous | Stable business category; a successful attempt uses ok |
| failure_origin | client / upstream / downstream / internal / null | Failure responsibility domain; legacy records may be null |
| failure_scope | request / model / credential / group / null | Smallest affected resource scope; null when not applicable or for legacy records |
| retry_directive | none / refresh_credential / next_candidate / null | Retry intent selected by Judge; legacy records may be null |
| effect | none / cooldown_credential / record_credential_failure / skip_group / null | The attempt's single runtime effect; legacy records may be null |
| rule_id | Stable rule identifier / null | Rule that produced the decision; legacy records may be null |
| will_retry | true / false | Whether another upstream attempt actually started afterward |
| dispatch_state | not_sent / maybe_sent / local / null | Definitely not sent, possibly reached the upstream, completed entirely inside GPT-Load, or unknown for a legacy record |
| response_started | true / false | Whether this attempt produced a response; local attempts may also be true |
| committed | true / false | Whether client output has started; candidates cannot be switched safely after commit |
| upstream_request_id | Upstream request ID / null | Use it with upstream support; null for local execution or when the upstream returned no ID |
| action | terminate / retry / cooldown_credential / fail_credential / skip_group | Compatibility display field; use retry_directive and effect for precise decisions |
Complete example §
status_code: 400 error_code: upstream_client_error failure_category: client_error failure_origin: upstream failure_scope: request retry_directive: none effect: none rule_id: fallback.http_client_error will_retry: false response_started: true committed: false
The upstream returned 400 and the problem affects only this request. The gateway does not switch credentials, cool down or blacklist the credential, or skip the Group. Inspect the redacted error summary and client request parameters.
Route-inspection reason codes §
Route inspection is a read-only simulation of current configuration and runtime state. It sends no upstream request, consumes no tokens, locks no credential, and writes no request log. The top-level reason explains the overall failure; Group and credential rows provide more detail.
| Reason code | Level | Meaning | How to handle |
|---|---|---|---|
| access_key_disabled | AccessKey | AccessKey has been deactivated | Enable it on the AccessKeys page |
| access_key_expired | AccessKey | AccessKey has expired | Create a new key or extend validity |
| protocol_filtered | AccessKey | This key has not selected this protocol | Add a check for the corresponding protocol in the key |
| model_filtered | AccessKey | Requested model is not within the allowed range | Check model limitations for the key |
| model_required_by_filter | AccessKey | The key restricts models, but the request carried no model name | Name the model explicitly in the request, or drop the key's model restriction |
| operation_unsupported | Request | No channel supports this operation for the current protocol | Use a Group that supports the capability |
| no_route_target | Request | No route target exists for this model or operation | Confirm that at least one Group exposes the model |
| no_available_group | Request | Route targets exist, but no Group is available | Inspect each Group's reason_code |
| native_route_required | Group | The request requires a native route, but this Group can only serve it through conversion | Use a Group whose protocol matches the client |
| group_disabled | Group | Group is deactivated | Enable this Group |
| group_filtered | Group | Group is not within the authorization scope of this key | Add this Group in the key |
| no_credentials | Group | There is not a single credential in the Group. | Add credentials to the Group |
| group_weight_zero | Group | A legacy configuration has a Group weight of 0 | Switch to automatic weight or a manual weight from 1 to 100 |
| no_available_credential | Group | Every credential in the Group is unavailable | Continue with credential-level reason_code values |
| credential_disabled | Credential | Credential has been deactivated | Enable it, or depend on other credentials |
| credential_auth_unavailable | Credential | Subscription account authorization has expired | Reauthorize, see subscription account page |
| credential_blacklisted | Credential | Credential has been blacklisted | Verify the credential, then restore it |
| credential_cooldown | Credential | Credential is in cooldown | Wait for automatic recovery, or add more credentials to share the load |
| credential_weight_zero | Credential | A credential weight of 0 left over from an old configuration | Switch to automatic weight or a manual weight from 1 to 100 |
The scheduler also defines credential_not_allowed to constrain a real request to the credential set captured when that request started. The current /api/route/inspect endpoint does not accept that request-scoped credential set, so it does not return this reason code.
After changing configuration, run route inspection again to verify current state without sending a real model request.
Retry and recovery §
- Request error — correct the request; do not switch credentials or change credential health.
- Rate limit — honor Retry-After; credential-scoped limits usually cause cooldown and another candidate attempt.
- Invalid credential — record a credential failure and blacklist it after the consecutive-failure threshold.
- Upstream host error — skip the current Group; replay is safe only for read-only operations or when the upstream proves it rejected processing.
- Streaming response — candidates cannot be switched after client output starts, preventing duplicate or corrupted output.
- Indeterminate management write — preserve and reuse the original Idempotency-Key and confirm operation state first.
View the complete scheduling, cooldown, and blacklisting behavior →