type, branch UX on code, surface message to humans, and quote request_id in support tickets.
Type taxonomy
type is one of five values. The Aurous-Request-Id response header always carries the same value as error.request_id so logging middleware doesn’t need to parse the body.
| Type | HTTP statuses | Example codes |
|---|---|---|
invalid_request | 400, 402, 409, 422 | missing_field, invalid_format, value_out_of_range, unsupported_lora_for_mode, idempotency_key_in_use, generation_not_cancellable, balance_too_low, prompt_blocked, reference_blocked, output_moderation_rejected, output_not_available |
authentication | 401 | missing_api_key, invalid_api_key, revoked_api_key |
not_found | 404, 410 | resource_not_found, forbidden_resource (404 by intent — no existence leak), output_expired (410) |
rate_limit | 429 | too_many_requests, concurrency_limit_exceeded |
server_error | 500, 502, 503, 504 | internal_error, provider_unavailable, provider_timeout |
Recommended retry policy
| Type | Retry? | How |
|---|---|---|
invalid_request | No. Fix the input. | param indicates the offending field. Don’t retry — the same body will fail again. |
authentication | No. Fix the key. | Re-fetch the key from your secret store; if revoked, mint a new one. |
not_found | No. | Resource doesn’t exist (or isn’t yours). Don’t retry. |
rate_limit | Yes with backoff. | Sleep Retry-After seconds (or X-RateLimit-Reset − now). See Rate limits. |
server_error | Yes with jitter. | Exponential backoff: 1s → 2s → 4s → 8s, max ~30s. With an Idempotency-Key, retries are safe — see Idempotency. |
One example per type
invalid_request — 400
authentication — 401
not_found — 404
A 404 covers both “doesn’t exist” and “exists but not yours.” We never reveal which — same Stripe stance, no existence-leak oracle.
rate_limit — 429
Retry-After seconds and retry. Don’t hammer — the bucket only refills at the sustained rate.
server_error — 5xx
Treat as transient. Retry with exponential backoff. If you sent an Idempotency-Key, the retry is safe even if the original request actually committed.
All error codes
Every error envelope’sdoc_url is https://docs.aurous-labs.com/errors#<code>. Each section below has an anchor matching the code so the link lands on the exact paragraph.
invalid_request codes — 400 / 402 / 409
missing_field
A required field was absent from the request body or query string. param names the field. HTTP 400. Don’t retry — supply the missing field.
invalid_format
A field is present but malformed (wrong shape, wrong enum value, wrong opaque-ID prefix). param names the field. HTTP 400.
value_out_of_range
A numeric or array-length field is outside its accepted range (e.g. count > 4, reference_image_urls.length > 6, guidance_scale < 1). HTTP 400.
parameter_invalid_combination
You sent two fields together that are mutually exclusive (e.g. size AND custom width/height on POST /v1/images). HTTP 400. param names the offending input. Pick one of the two paths per request.
mutually_exclusive_input
You sent two body inputs that the endpoint accepts independently but rejects together (e.g. character_id AND reference_image_urls on POST /v1/images). HTTP 400. Distinct from parameter_invalid_combination — this code is reserved for body-input pairs whose semantic disambiguation requires you to pick one. param names the offending input.
missing_field
(See missing_field above. Same code is also returned when a half-supplied input is detected — e.g. width without height on POST /v1/images.)
character_not_ready
You referenced a character_id that exists but is not in status: ready (it’s synthesizing, reviewing, failed, or soft-deleted). HTTP 400. Wait for the character’s status to flip via GET /v1/characters/{id} or webhook, or pick a different character.
unsupported_lora_for_mode
The LoRA you referenced isn’t compatible with the inference mode the request would dispatch (image LoRA on /v1/videos, or vice versa). HTTP 400. Pick a LoRA from the matching catalog.
generation_not_cancellable
You called POST /v1/images/{id}/cancel on a generation that’s already terminal (succeeded, failed, cancelled, expired, or moderation_rejected). HTTP 400. Idempotent — calling cancel on a row whose hold already resolved is a no-op.
prompt_blocked
Pre-dispatch moderation classifier rejected the prompt. HTTP 400. No row is inserted, so there’s nothing to retrieve via GET /v1/images/{id}. (A future date-pin will insert a moderation_rejected row and fire image.moderation_rejected instead — see the Changelog.)
reference_blocked
Pre-dispatch moderation classifier rejected one of the reference images. HTTP 400. Same disposition as prompt_blocked.
output_moderation_rejected
Post-generation classifier rejected the output. HTTP 400. The hold is released; no charge. The reason ID is logged on the inference row.
unknown_version
The Aurous-Version header value is not in the published catalog (see the Changelog). HTTP 400. Use a date-pin advertised on the changelog or omit the header to fall back to your team default.
balance_too_low
The team’s available balance (credits − pending holds) is less than the cost of the requested generation. HTTP 402. Top up via the dashboard or wait for pending holds to commit/release.
idempotency_key_in_use
Returned in three cases, all HTTP 409: (1) you sent the same Idempotency-Key with a different request body; (2) you reused the same key across different routes (e.g. /v1/images and /v1/videos); or (3) a request with this key is still in flight — the first call hasn’t finished yet. For (1) and (2), use a fresh key (or resend the original body to replay). For (3), wait briefly and retry the same key; once the original completes you’ll receive its replayed response. See Idempotency.
authentication codes — 401
missing_api_key
No X-Api-Key header was sent. HTTP 401. Add the header — see Authentication.
invalid_api_key
The X-Api-Key header value is malformed, unknown, or no longer authorized for the requested route. HTTP 401. Re-fetch the key from your secret store; if it was rotated, mint a new one in the dashboard.
revoked_api_key
The key existed but has been revoked. HTTP 401. Mint a new key in /dashboard/api-keys.
not_found codes — 404
resource_not_found
The resource doesn’t exist (or doesn’t belong to the requesting team — see forbidden_resource). HTTP 404. Don’t retry.
forbidden_resource
The resource exists but belongs to another team. HTTP 404 (we return 404 instead of 403 — same Stripe stance, no existence-leak oracle).
output_expired
The generation reached status: succeeded and produced output, but the stored output URL has aged past its retention window. HTTP 410.
- Image outputs are retained ~7 days after generation.
- Video outputs are retained ~24 hours after generation.
GET /v1/images/{id}/output/{n} and GET /v1/videos/{id}/output return 410 Gone with this code. Save copies of outputs you want to keep — long-term storage is intentionally not part of the platform. To get fresh outputs, create a new generation with the same prompt.
output_not_available (422) — terminal-status-without-output
output_not_available
The generation reached a terminal status that never produced output (failed, cancelled, moderation_rejected, or polling-timeout expired) — or is still in-flight (pending / processing). HTTP 422.
Distinct from output_expired: output_expired means the URLs once existed and aged out, output_not_available means they never existed. Check GET /v1/images/{id} (or GET /v1/videos/{id}) for the row’s status and (when failed) error_message, then create a new generation.
rate_limit codes — 429
too_many_requests
You exceeded the rate limit for this endpoint class. HTTP 429. Sleep Retry-After seconds and try again. See Rate limits.
concurrency_limit_exceeded
You exceeded the per-team concurrent-in-flight cap (default 10 generations in pending/processing). HTTP 429. Wait for in-flight work to settle and retry; or apply backpressure in your client.
server_error codes — 5xx
internal_error
The platform hit an unexpected condition. HTTP 500. Retry with exponential backoff. With an Idempotency-Key, retries are safe.
provider_unavailable
The upstream image / video provider is unhealthy. HTTP 503. Retry with exponential backoff.
provider_timeout
The upstream image / video provider didn’t return within the platform’s polling window. HTTP 504. Retry with exponential backoff.
chat_provider_unavailable
The upstream chat model is temporarily unavailable. HTTP 502. Any held credits are released. Retry with exponential backoff — with an Idempotency-Key on a non-streamed request, retries are safe. See also: chat_provider_unavailable.
chat_provider_request_invalid
The platform sent a malformed request to the upstream model. HTTP 500. Treated as a platform-side bug; engineering is paged. Held credits are released. Retry with backoff. See also: chat_provider_request_invalid.
chat_provider_auth_failed
The platform’s credential with the upstream model failed. HTTP 500. Not a problem with your X-Api-Key. On-call paged. Retry after a short delay. See also: chat_provider_auth_failed.
chat_provider_unknown_error
The upstream returned an error the platform’s mapping table doesn’t yet recognize. HTTP 502. Engineering will add the mapping; treat as transient. See also: chat_provider_unknown_error.
LLM chat + embeddings — invalid_request codes
model_not_found
The model slug is unknown for your team. HTTP 404. List available models with GET /v1/models. See also: model_not_found.
model_disabled
The model exists but has been deactivated. HTTP 403. Pick a different model from the listing. See also: model_disabled.
model_wrong_kind
You sent an embedding model to the chat endpoint, or vice versa. HTTP 400. Check aurous_metadata.kind on each model row. See also: model_wrong_kind.
max_tokens_exceeds_hard_cap
max_tokens exceeds the model’s max_output_tokens_hard_cap. HTTP 400. Lower the request or pick a larger-cap model. See also: max_tokens_exceeds_hard_cap.
missing_max_tokens_no_model_default
max_tokens was omitted on a model with no platform default. HTTP 400. Pass max_tokens explicitly. See also: missing_max_tokens_no_model_default.
max_input_tokens_exceeded
Prompt is over the model’s context window. HTTP 400. Trim input or pick a larger model. See also: max_input_tokens_exceeded.
tool_choice_required_unsupported
tool_choice: "required" requested on a model whose capabilities don’t include it. HTTP 400. Use tool_choice: "auto" or pick a capable model. See also: tool_choice_required_unsupported.
response_format_too_large
JSON schema in response_format exceeds the platform’s payload cap. HTTP 400. Trim the schema. See also: response_format_too_large.
response_format_too_deep
JSON schema in response_format nests deeper than the parser’s cap. HTTP 400. Flatten via $defs references. See also: response_format_too_deep.
chat_cancel_target_not_found
The cancel id doesn’t exist for your team. HTTP 404. No existence leak across teams. See also: chat_cancel_target_not_found.
chat_cancel_target_already_terminal
The chat completion is already in a terminal state. HTTP 409. Idempotency hint, not a bug. Read the final-state record. See also: chat_cancel_target_already_terminal.
chat_cancel_target_not_cancellable
The record is non-terminal but the cancel can’t take effect (sync call already returned, or in-flight on a different deploy instance). HTTP 409. See also: chat_cancel_target_not_cancellable.
LLM embeddings — invalid_request codes
embeddings_batch_not_supported
input was sent as an array of pure strings. HTTP 400. v1.0 multimodal embeddings would concatenate batched text into one combined vector (opposite of OpenAI’s N→N semantics), so the platform rejects the shape explicitly. Loop client-side for N→N, or pass a content-parts array for one combined embedding. See also: embeddings_batch_not_supported.
embeddings_input_too_many_items
input content-parts array exceeds the per-request caps (16 total parts, 8 image_url parts). HTTP 400. Split into multiple requests. See also: embeddings_input_too_many_items.
embeddings_video_unsupported
video_url parts are not accepted on v1 embeddings as of 2026-05-24. The provider folds video frames into the visual billing bucket, so the previously published video rate never actually fired — to keep the receipt honest we removed the shape. Submit text or image_url parts only; image inputs bill at the visual rate. HTTP 400. See also: embeddings_video_unsupported.
Renamed 2026-05-24 from embeddings_video_too_many_parts (which previously fired only on 2-or-more videos). Integrations that caught the old code on a single-video payload should switch to embeddings_video_unsupported and remove the video_url part entirely.
LLM embeddings — server_error codes
embeddings_provider_unknown_error
The upstream embedding model returned an error the platform’s mapping table doesn’t yet recognize. HTTP 502. Engineering will add the mapping; treat as transient and retry with backoff. See also: embeddings_provider_unknown_error.
LLM chat + embeddings — rate_limit codes
tpm_rate_limit_exceeded
Tokens-per-minute bucket exhausted for your team. HTTP 429. Sleep Retry-After seconds; see Rate limits. See also: tpm_rate_limit_exceeded.
provider_rate_limited
The upstream model is throttling. HTTP 503. Retry with backoff; Retry-After is forwarded when available. See also: provider_rate_limited.
The doc_url convention
Every error code has a deterministic deep-link: https://docs.aurous-labs.com/errors#<code>. Open it in a browser to land on this page’s anchor for the code.
Using request_id for support
Every error envelope and every successful response carries a request_id (also surfaced as the Aurous-Request-Id response header). When opening a support ticket, paste at least one request_id so we can pull the exact request from server logs. Example:
Hi support — gettingThis shortcuts triage from “let’s look around” to “here’s the exact log line.”provider_timeoutonPOST /v1/imagesfor the last hour.request_id: req_01HXMQ7Z3K8Y2ABCDEFGHJKM. Teamacme. Thanks.
Idempotency replay
When you retry with the sameIdempotency-Key:
- Success (
2xx) is replayed byte-for-byte withAurous-Idempotent-Replayed: true, and is never re-billed — even if the original request had already committed. invalid_request(4xx) is not replayed.balance_too_low,prompt_blocked,invalid_format, and the rest of theinvalid_requestfamily are client-fixable: a same-key retry re-evaluates the corrected request rather than replaying the old rejection. Fix the input (top up, edit the prompt) and retry with the same key.- Transient server errors (
5xx) are safe to retry with the same key and backoff — the key ensures you won’t create a duplicate generation or double-charge if the original had actually committed.
idempotency_key_in_use is the one invalid_request code you may see on a retry — it’s raised by the idempotency layer itself, not replayed from cache.) See Idempotency.
Headers on every response
Aurous-Request-Id: req_<ulid>— quote this in support tickets.Aurous-Version: YYYY-MM-DD— the API version pin applied to this response.X-RateLimit-Limit/X-RateLimit-Remaining/X-RateLimit-Reset— see Rate limits.Retry-After(only on429) — seconds to wait before retrying.Aurous-Idempotent-Replayed: true(only on idempotency-key replays) — see Idempotency.

