Errors
The error envelope
Section titled “The error envelope”Every application-level error — bad requests, auth failures, missing resources, rate limits, upstream failures — is returned as JSON with a non-2xx status code, in one consistent envelope:
{ "error": { "type": "not_found", "code": "no_such_avatar", "message": "No avatar with id 'avt_doesnotexist' was found.", "request_id": "req_5f3a1c9e2b7d" }}| Field | Description |
|---|---|
type | Broad error category. Stable, safe to branch on in code. |
code | Specific machine-readable reason within that category. Stable. |
message | Human-readable explanation. Don’t parse this; it can change wording without notice. |
request_id | Unique ID for this request, also returned as the x-request-id response header. Include it when contacting support. |
Branch on the HTTP status code and error.type (and error.code where you need finer granularity), never on the exact text of message.
Error types and codes
Section titled “Error types and codes”| HTTP | error.type | error.code | Meaning |
|---|---|---|---|
| 401 | authentication | missing_key | No Authorization: Bearer sk_... header was sent. |
| 401 | authentication | invalid_key | The key is unknown or has been revoked. |
| 404 | not_found | no_such_session | The session doesn’t exist, or isn’t yours. |
| 404 | not_found | no_such_avatar | No such avatar id. |
| 404 | not_found | no_such_voice | No such voice id. |
| 404 | not_found | no_such_file | No such file id. |
| 404 | not_found | no_such_waiting_state | The named waiting state isn’t configured for this avatar. |
| 409 | conflict | session_ended | The session has already ended. Create a new one. |
| 409 | conflict | stream_not_connected | No browser is attached to this session’s WebSocket yet. Wait for session.ready. |
| 422 | invalid_request | invalid_body | The request body is missing or not valid JSON. |
| 422 | invalid_request | empty_text | text was empty. |
| 422 | invalid_request | empty_audio | The audio chunk contained no samples. |
| 422 | invalid_request | chunk_too_large | The audio chunk exceeded the per-chunk limit. |
| 422 | invalid_request | file_too_large | The uploaded file exceeded the size limit. |
| 422 | invalid_request | missing_name | A required name was not supplied. |
| 422 | invalid_request | missing_source | Neither a file_id nor a url was supplied. |
| 422 | invalid_request | mode_not_available | The requested mode isn’t available for this avatar. |
| 422 | invalid_request | voice_too_quiet | The reference audio’s level was too low to clone from. |
| 422 | invalid_request | voice_unprocessable | The reference audio couldn’t be decoded or processed. |
| 429 | rate_limit | concurrency_cap | You’re at your key’s concurrent-session limit. |
| 429 | rate_limit | platform_busy | The platform is at global capacity. Retry shortly. |
| 500 | server | internal_error | Something went wrong on our end. Safe to retry with backoff. |
| 502 | server | muse_error / muse_unreachable | The rendering stack failed or was unreachable. |
| 502 | server | qwen_bad_response | The speech backend returned an unusable response. |
| 503 | server | voices_degraded / files_degraded | That subsystem is temporarily unconfigured or unavailable; the rest of the API still works. |
Retries
Section titled “Retries”Both 429 responses carry a Retry-After: 10 header. Honour it rather than retrying immediately — retrying in a tight loop will keep you capped.
concurrency_cap— you’re at your own limit. End a session you no longer need, or wait for one to finish.platform_busy— the platform as a whole is saturated. Back off and retry.
500 and 502 are safe to retry with exponential backoff. 503 means a subsystem is degraded; retry, but expect it to persist until the subsystem is restored. 4xx responses other than 429 will not succeed on retry — fix the request.
- Limits & concurrency for the specific case of
429s.