Skip to Content
Errors

Errors

The native endpoints (/v1/tts, /v1/stt) return errors in one consistent JSON structure. The compatible APIs re-wrap the same catalog codes in their vendor’s envelope (OpenAI error.code, ElevenLabs detail.status, Cartesia error_code), so everything on this page applies there too.

Error response shape

{ "error": { "code": "<error-code>", "message": "<human-readable description>", "request_id": "<unique request identifier>" } }
  • code — machine-readable code. Use this field in your error-handling logic (not message, which may change).
  • message — human-readable description intended for logs and debugging.
  • request_id — include this when contacting support to help us trace the request.

Error catalog

unauthorized — HTTP 401

The API key is missing, malformed, invalid, revoked, or expired.

Common causes

  • Authorization header is absent
  • Bearer token does not start with the rvl_live_ prefix
  • Key was deleted from the dashboard or has expired

Recovery — Send Authorization: Bearer rvl_live_<your-key> and confirm the key is active in your API Keys page.

{ "error": { "code": "unauthorized", "message": "Invalid API key format. Keys must start with 'rvl_live_'.", "request_id": "req_01HXYZ..." } }

forbidden — HTTP 403

The API key is valid but its scope does not permit this endpoint.

Common causes

  • A tts-only key called POST /v1/stt (or vice versa)
  • The key lacks the scope required by the route

Recovery — Use a key whose scope includes this endpoint, or create a new key with the required scope.

{ "error": { "code": "forbidden", "message": "API key scope does not permit this endpoint.", "request_id": "req_01HXYZ..." } }

validation_error — HTTP 400 / 413 / 415

The request body or file is invalid.

Common causes

  • Unknown model name (400)
  • Text exceeds the 500-character limit (400)
  • Invalid voice_id — not in the voice catalog (400)
  • speed is outside the 0.5–2.0 range (400)
  • STT file exceeds 50 MB or the 30-minute duration cap (413, or 400 when the over-length duration is detected by probing)
  • Unsupported or mismatched audio format (415)

Recovery — Check the error message for the specific field that failed validation. Consult the TTS API or STT API reference for allowed values.

{ "error": { "code": "validation_error", "message": "Unknown model 'nada-2.0'. Valid models: ['nada-1.0-flash-lite', 'nada-1.0-flash', 'nada-1.0-pro']", "request_id": "req_01HXYZ..." } }

model_rejected — HTTP 422

The upstream model rejected the request payload.

Common causes

  • Input the model could not process
  • Unsupported combination of parameters for the selected model

Recovery — Adjust the request per the message and retry. This is not retryable unchanged.

{ "error": { "code": "model_rejected", "message": "The model rejected the request.", "request_id": "req_01HXYZ..." } }

malware_detected — HTTP 422

The uploaded audio file failed GuardDuty malware scanning.

Common causes

  • File contains malicious content
  • File was tampered or corrupted in transit

Recovery — Do not retry with the same file. If you believe this is a false positive, contact support with the request_id.

{ "error": { "code": "malware_detected", "message": "The uploaded file failed malware scanning.", "request_id": "req_01HXYZ..." } }

insufficient_balance — HTTP 402

The organization’s usage allowance is exhausted.

Common causes

  • The available usage allowance has been fully consumed

Recovery — wait for your usage allowance to reset, or contact support to increase it.

{ "error": { "code": "insufficient_balance", "message": "Insufficient balance to complete this request.", "request_id": "req_01HXYZ..." } }

rate_limited — HTTP 429

A rate limit was exceeded — request count or usage units (audio-minutes) — in one of the active windows (per-minute and per-day, with per-model limits possible on top).

Common causes

  • Too many requests in the current per-minute or per-day window
  • The audio-minute (usage-unit) allowance for the window is used up
  • A per-model limit tripped even though the overall limit still had room
  • Burst traffic without backoff

Recovery — Honor the Retry-After header (seconds until the soonest tripped limit resets), then retry with exponential backoff plus jitter. Do not retry non-429 errors this way — only 429s are safe to retry unchanged. Note that rejected (429) requests still count toward your request limits, so hammering a tripped limit extends the outage. Watch the X-RateLimit-* response headers (below) to stay under your limits; contact Revolab for a higher tier.

{ "error": { "code": "rate_limited", "message": "Rate limit exceeded.", "request_id": "req_01HXYZ..." } }

quota_exceeded — HTTP 429

The monthly quota for the requested model has been exhausted. This applies to the free models only — see Free allowances.

Common causes

  • The 60-minute monthly allowance for a free model (nada-1.0-flash-lite, aisyah-1.0-flash) is used up

Recovery — Wait for the quota to reset at the start of next month, or switch to the equivalent paid model, which bills against your balance instead of a monthly cap. Honor the Retry-After header.

{ "error": { "code": "quota_exceeded", "message": "Monthly quota for model 'nada-1.0-flash-lite' has been exhausted.", "request_id": "req_01HXYZ..." } }

concurrency_limit_exceeded — HTTP 429

Too many requests are in flight at once for your key.

Common causes

  • More concurrent requests than your concurrency cap allows

Recovery — Reduce in-flight concurrency and retry after the Retry-After delay.

{ "error": { "code": "concurrency_limit_exceeded", "message": "Too many concurrent requests. Retry shortly.", "request_id": "req_01HXYZ..." } }

endpoint_unavailable — HTTP 502

The upstream TTS or STT model endpoint returned an error or malformed audio.

Common causes

  • Model server is temporarily overloaded
  • TTS endpoint returned audio with an unparseable WAV header
  • STT model returned an unexpected response format

Recovery — Retry after a short delay. If the issue persists, check the status page or contact support.

{ "error": { "code": "endpoint_unavailable", "message": "The upstream TTS service returned an unexpected response.", "request_id": "req_01HXYZ..." } }

voice_not_ready — HTTP 503

The requested voice is still enrolling and cannot synthesize yet.

Common causes

  • A cloned voice is still processing after creation

Recovery — Wait until the voice finishes enrolling (check the Voices page), then retry.

{ "error": { "code": "voice_not_ready", "message": "Voice is not available yet.", "request_id": "req_01HXYZ..." } }

service_unavailable — HTTP 503

The TTS or STT service is under maintenance or its configuration is missing.

Common causes

  • Planned maintenance window
  • Endpoint configuration not yet provisioned in this region

Recovery — Wait and retry. Check the status page for maintenance windows.

{ "error": { "code": "service_unavailable", "message": "TTS service is temporarily unavailable.", "request_id": "req_01HXYZ..." } }

Rate limit headers

Rate-limited endpoints return two families of headers so your client can pace itself instead of discovering limits through 429s. The request family is always present; the units family appears when an audio-minute (usage-unit) limit applies to the request. Each family reports the tightest (soonest-limiting) window.

HeaderMeaning
X-RateLimit-LimitRequest allowance in the tightest window
X-RateLimit-RemainingRequests left before a 429
X-RateLimit-ResetSeconds until the request window resets
X-RateLimit-Limit-UnitsAudio-minute allowance in the tightest window
X-RateLimit-Remaining-UnitsAudio-minutes left before a 429
X-RateLimit-Reset-UnitsSeconds until the unit window resets

On a 429, Retry-After carries the seconds until the soonest tripped limit resets — always prefer it over a fixed sleep.

FastAPI validation errors (422)

If the request body is structurally invalid (e.g., JSON parse failure or a required field is missing), FastAPI returns a 422 with a detail array before the gateway error contract is applied:

{ "detail": [ { "loc": ["body", "text"], "msg": "field required", "type": "value_error.missing" } ] }

This happens at the framework layer before the endpoint handler runs. Handle it by checking for the presence of error vs detail in the response body.