Build your integration
Markdown ↗

Errors & recovery

Understand what happened and choose the right next step.

First distinguish a rejected HTTP request from a generation that was accepted and failed later. They need different recovery actions.

Read an API error

Rejected API requests use an error object:

Example response
{
  "error": {
    "code": "validation_error",
    "message": "The request contains invalid fields.",
    "retryable": false,
    "errors": [
      {
        "pointer": "/quality",
        "code": "invalid_value",
        "message": "Choose a value supported by this model."
      }
    ]
  }
}

Use error.code for program logic and error.message to explain the problem. Validation errors can include errors, with a JSON Pointer such as /quality identifying the field. Do not depend on exact English message text.

The X-Request-Id response header identifies the API request. Keep it with the HTTP status, error code, method, timestamp and run ID when available. Never include your API key in logs or support reports.

Choose the next step

HTTP status Typical cause What to do
400 Malformed request or conflicting authentication headers. Correct the request; send one supported authentication header.
401 Missing or invalid API key. Check the key and header. Signing into the website does not authenticate the API.
402 insufficient_balance. Add funds before submitting again.
403 Account access is denied, or the media source blocks automated downloads. Check resource access; source URLs must be publicly downloadable.
404 Resource is missing, inaccessible, or the source URL has expired. Check the ID and owner, or replace the source URL.
409 request_in_progress: the original submission is still being accepted. Wait for Retry-After, then retry with the same Idempotency-Key and unchanged body.
409 run_not_cancelable. Read the run's current state; do not retry cancellation automatically.
410 cursor_expired. Restart history from your original filters.
410 media_expired: a referenced input has expired. Replace the input with available media or import the original file again. Repeating the expired reference will not help.
413 request_too_large or remote_media_too_large. Reduce the JSON body to at most 1 MiB, or use a smaller source file, respectively.
415 Unsupported request content type. Send JSON with Content-Type: application/json.
422 idempotency_conflict: the key belongs to a different request body. Restore the original body to recover that submission. Use a new key only when you intend to submit a different request; do not switch keys automatically.
422 estimate_requires_authentication. Supply a valid API key to estimate with media or LoRA inputs.
422 Invalid or unsupported input. Fix the fields indicated by the error. Consult the model's current schema.
429 Too many requests. Wait, respect Retry-After when present and reduce concurrency.
502 / 504 remote_media_provider_unavailable / remote_media_timeout. The source download failed temporarily. Respect any retry delay and follow the safe retry rules below.
503 A service is unavailable. Inspect error.code and error.retryable: maintenance can be temporary; remote_media_provider_not_configured requires another source or support.
Other 5xx Unexpected service or gateway failure. The outcome may be uncertain. Follow the safe retry rules below.

HTTP errors and generation errors use the same general error shape, but a failed run is returned inside a successful read response.

When a submission times out

A timeout, interrupted connection or gateway error does not prove that a generation was rejected. The run may already exist.

  • You have the run ID: read that run or wait for its webhook.
  • You sent an idempotency key: repeat the original request with the same key and body after a delay.
  • You have neither: inspect your recent run history before resubmitting. An unkeyed repeat can create and charge for a second generation.

For future requests, save an idempotency key and request body before sending. Do not switch to a new key after a timeout.

Retry temporary failures

error.retryable indicates whether the API classifies the problem as potentially temporary. It is not permission to create another generation blindly.

For 409 request_in_progress, the server returns retryable: true and Retry-After: 2. Wait at least two seconds before repeating the same submission with its original key and body. A new key would create a separate submission and could generate and charge twice. Choose recovery by error.code, not HTTP status alone.

For reads, estimates and safe idempotent replays, use bounded exponential backoff with jitter and respect Retry-After. Set a maximum waiting time appropriate to your application. Avoid tight retry loops and simultaneous retries from many workers.

Errors such as execution_paused, webhook_unavailable, balance_unavailable, remote_media_provider_unavailable and remote_media_timeout can include a retry delay. Continue to use the same idempotency key and unchanged body when retrying a submission. A 5xx status alone does not mean the error is automatically retryable.

An intermediary can return a non-JSON error page. Check the HTTP status and content type before parsing; keep enough diagnostic information to distinguish a gateway problem from an API validation error.

A generation failed after acceptance

Example response
{
  "id": 123456,
  "status": "failed",
  "cost": {
    "unit": "common_fuz",
    "expected": "40.0000",
    "status": "settled",
    "charged": "0.0000"
  },
  "error": {
    "code": "execution_failed",
    "message": "The run could not be completed."
  }
}

GET /run/{id} still returns HTTP 200 because the run was retrieved successfully. Inspect status and error, not just the HTTP response.

Keep any usable partial outputs and read the returned cost. Repeating a GET cannot restart the generation. If you decide to try generating again after reviewing the failure, submit a new request with a new idempotency key.

Common integration mistakes

  • A field is rejected: use the exact flat field names from the model's request_schema. Fields vary between models and versions.
  • Model discovery works but generation returns 401: discovery is public; generation requires an API key.
  • The request schema changes with quality: selector defaults and conditional rules are part of the model schema. Do not hard-code one inference-step default for every quality preset.
  • History pagination returns 422: follow the cursor URL unchanged, with no extra filters. Cursors belong to the account that created them.
  • No webhook arrived: verify your public HTTPS receiver returns 2xx; use the existing run ID for recovery. A missing callback is not a reason to submit again.
  • A file URL stopped working: check url_expires_at; save outputs in your own storage before expiration.