# Errors

Most errors are a JSON envelope:

```json
{
  "error": {
    "statusCode": 402,
    "message": "Insufficient credit",
    "code": "insufficient_credit"
  }
}
```

`message` is human-readable; `code` is stable and machine-readable.
429 responses additionally carry `retryAfterSeconds` in the envelope and
a `Retry-After` header. 422 responses carry a
`details` array naming each invalid field. Exception: failures raised
before body parsing (missing/invalid API key, oversized body) return a flat
`{"message", "code"}` without the wrapper.

## Status codes

| Status | Meaning | What to do |
| --- | --- | --- |
| 400 | Request violates the model's contract (unsupported attribute, value out of bounds). Nothing charged. | Fix the request; the message names the field. |
| 401 | Missing, invalid, revoked or expired API key. | Check the Authorization header and key status. |
| 402 `insufficient_credit` | Available credit cannot cover the request's worst case. | Add credit, or lower max_tokens. |
| 403 | Key or account not allowed: `insufficient_scope`, model not on the allowlist, `key_spend_limit_exceeded`, `request_cost_limit_exceeded`, or frozen account (`account_not_active`). | The code names the cause; raise the limit, wait for the window reset, or use a differently-scoped key. |
| 404 | Unknown model, job or artifact, or one owned by another account. | Check the id. |
| 409 | Request state conflict; needs review, not a blind retry. | Check the job or contact support. |
| 422 `validation_failed` | Body failed validation before dispatch. | The `details` array lists each field and problem. |
| 429 + Retry-After | The key's requests-per-minute limit was hit. | Wait `Retry-After` seconds. |
| 5xx / 503 | Platform or network cannot serve this now, including no provider online. Nothing charged. | Retry with backoff; check provider count on the catalog. |

## Error code catalog

Every stable code, its status and whether a retry can help:

| Code | Status | Retryable | Meaning |
| --- | --- | --- | --- |
| `content_policy_violation` | 403 | no | The request clearly asks for sexual exploitation of minors, targeted violence, fraud or credential theft. It was not run and you were not charged; the error includes the policy and request ID. |
| `policy_review_unavailable` | 503 | yes | A safety review could not finish. The request was not run and you were not charged. Retry after a short delay; the error includes retryable: true and a request ID. |
| `missing_api_key` | 401 | no | No Authorization header was sent. |
| `invalid_api_key` | 401 | no | The key is unknown, revoked or expired. |
| `insufficient_scope` | 403 | no | The endpoint family is outside the key's scopes. |
| `model_not_allowed` | 403 | no | The model is not on the key's allowlist. |
| `key_spend_limit_exceeded` | 403 | no | The key's spend budget for the current window is used up. |
| `request_cost_limit_exceeded` | 403 | no | The request's worst-case cost exceeds the key's per-request cap. |
| `retail_cost_limit_exceeded` | 403 | no | The request's worst-case retail hold exceeds X-OpenMayhem-Max-Cost-Usd. |
| `price_ceiling_exceeded` | 403 | no | The model's current retail price is above a price ceiling header; the error names the model, field, ceiling and current price. |
| `account_not_active` | 403 | no | The account is frozen or closed. |
| `insufficient_credit` | 402 | no | Available credit cannot cover the request's worst case. |
| `invalid_content_length` | 400 | no | The Content-Length header is missing or malformed. |
| `request_body_too_large` | 413 | no | The body exceeds the endpoint's size limit. |
| `contract_attribute_required` | 400 | no | A required attribute of the model's signed contract is missing. |
| `invalid_request` | 400 | no | The request violates the model's signed contract; the message names the field. |
| `invalid_catalog_filter` | 400 | no | A catalog query filter has an invalid name, value or combination. |
| `invalid_routing_control` | 400 | no | A hosted routing header has an invalid value. |
| `routing_control_not_public` | 400 | no | A customer attempted to set a platform-controlled routing header. |
| `routing_control_not_supported_for_endpoint` | 400 | no | A context or hedging control was used on a non-text endpoint family. |
| `incompatible_routing_constraints` | 400 | no | No registered route satisfies the supplied combination of otherwise valid constraints. |
| `validation_failed` | 422 | no | Body validation failed before dispatch; details lists each field. |
| `model_not_found` | 404 | no | The model id is unknown or the model is disabled. |
| `job_not_found` | 404 | no | The job id is unknown or belongs to another account. |
| `request_not_found` | 404 | no | The request id is unknown or belongs to another account. |
| `artifact_not_found` | 404 | no | The artifact id is unknown or belongs to another account. |
| `artifact_expired` | 404 | no | The artifact is past its retention window and was deleted. |
| `endpoint_not_supported` | 404 | no | The model does not serve this endpoint family. |
| `token_count_unsupported` | 400 | no | The selected model runtime does not expose an exact native tokenizer; no estimate is returned. |
| `token_count_failed` | 503 | yes | The selected tokenizer provider could not complete the control request; retry with backoff. |
| `idempotency_in_flight` | 409 | yes | A request with the same Idempotency-Key is still running; retry shortly. |
| `idempotency_conflict` | 409 | no | The Idempotency-Key was already used for a different body or routing requirement. |
| `idempotency_replay_unavailable` | 409 | no | The request completed and duplicate billing was prevented, but inference content is not retained; use a new key for a new invocation. |
| `rate_limited` | 429 | yes | The key's requests-per-minute limit was hit; wait Retry-After seconds. |
| `client_closed` | 499 | no | The caller disconnected before the request completed. |
| `state_conflict` | 502 | no | The platform and network disagree about this request's state; needs review, do not retry blindly. |
| `upstream_error` | 502 | yes | The provider or network failed mid-request. |
| `capacity_unavailable` | 503 | yes | No provider capacity for this model right now; retry with backoff. |
| `no_provider_satisfies_attestation_tier` | 400 | no | No registered provider satisfies the minimum attestation tier. |
| `no_provider_satisfies_quant` | 400 | no | No registered provider serves the required quantization. |
| `context_requirement_unavailable` | 400 | no | No registered provider has the required context capacity. |
| `preferred_provider_unavailable` | 503 | yes | None of the explicitly preferred providers is currently eligible. |
| `context_capacity_unavailable` | 503 | yes | The requested context capacity is registered but unavailable right now. |
| `no_provider_within_price_band` | 503 | yes | No route satisfies the platform-controlled wholesale rate limit. |
| `confidential_compute_unavailable` | 503 | yes | Prompt-confidential compute is registered but unavailable right now. |
| `api_key_verification_unavailable` | 503 | yes | Key verification is temporarily unavailable; retry with backoff. |

The same catalog is machine-readable in the OpenAPI spec:
https://openmayhem.ai/openapi.json

## Errors and your money

A request rejected before reaching a provider never charges. When an
outcome is ambiguous (connection lost mid-generation), the hold stays until
the provider's receipt resolves what was done; the receipt amount is what
you pay, and a hold with no receipt is released.

<a id="media-troubleshooting"></a>

## When a media request cannot run

Invalid custom size or unavailable estimate: return to a supported preset, then check width, height, steps and batch count against both the model contract and current provider capacity. A selectable model does not guarantee a provider can run every combination right now.

Missing LoRA or reference control: check whether this workflow offers the capability. Select an approved option or a compatible workflow; adding unsupported JSON fields does not enable it.

Insufficient credit: compare your available balance with the maximum hold, including other active requests. A price-ceiling rejection is different: review your routing or key limits against the tariff reference.

Failed or interrupted run: inspect the existing job and its error before submitting again. Completed work can still be charged. Save the job id, selected settings and error code when reporting an issue, and omit API keys and private input files.

- [Presets, dimensions and repeatability](https://openmayhem.ai/docs/media#media-settings)
- [Media estimates and credit holds](https://openmayhem.ai/docs/billing#media-estimates)
