VoiceMaker

Error Codes

VoiceMaker returns standard HTTP status codes along with a consistent JSON error body. Every error carries a stable, machine-readable errorCode — branch on it rather than on the error/detail text, which may be reworded — and a requestId you can quote when contacting support.

Error response format

JSON
{
  "error": "Invalid request",
  "detail": "Request body did not match the expected schema.",
  "errorCode": "invalid_payload",
  "requestId": "8f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
  "issues": [
    { "field": "voice_id", "code": "too_small", "message": "Required" }
  ]
}
FieldAlways presentDescription
erroryesShort human-readable title.
detailyesWhat went wrong and, where possible, what to do.
errorCodeyesStable snake_case code from the catalog below. Never renamed once shipped; new codes may be added.
requestIdyesRequest id (also sent as the X-Request-Id header).
issuesvalidation errors onlyThe offending fields as { field, code, message }.

Rate-limit responses (429) and cold-start responses (503) also send a Retry-After header with the number of seconds to wait.

HTTP status codes

StatusMeaningWhen it happens
200OKSynchronous request succeeded.
201CreatedStream token minted.
202AcceptedAsync job queued (transcription, animation, async TTS).
400Bad RequestInvalid JSON, schema violation, unsupported language, missing voice_id.
401UnauthorizedMissing, invalid, or expired API key or stream token.
402Payment RequiredNot enough purchased credits for the request.
403ForbiddenKey revoked, project disabled, plan not API-enabled, missing scope, or a stream token of the wrong kind.
404Not FoundJob does not exist for your project, or no such endpoint.
413Payload Too LargeAudio exceeds your plan's (or the endpoint's) file-size limit.
415Unsupported Media TypeWrong Content-Type, or a media file that is not audio/video.
422Unprocessable EntityValid request but rejected values (unknown voice_id, unreachable audio URL).
426Upgrade RequiredA streaming endpoint was called without a WebSocket upgrade.
429Too Many RequestsRate limit, queue, concurrency or concurrent-stream limit reached.
500Internal Server ErrorUnexpected processing error — safe to retry.
503Service UnavailableEngine warming up or a dependency temporarily unavailable — retry after Retry-After.

Error-code catalog

The SDK exception column is the class the official SDKs raise for the code (same names in JavaScript and Python; every SDK error also exposes the code as err.code / e.code). Subclasses are listed where they exist — for example UnknownVoiceError is also a ValidationError, and ServiceWarmingError is also a ServiceUnavailableError.

Authentication & authorisation

CodeStatusMeaningWhat to doSDK exception
missing_api_key401No API key was sent with the request.Send the key in the Authorization: Bearer header.AuthenticationError
invalid_api_key401The API key is malformed, unknown, or belongs to the other environment (test vs live).Check the key for typos — keys look like vmk_live_… or vmk_test_…. Create one in the dashboard under Developer → API keys.AuthenticationError
api_key_expired401The API key has passed its expiry date.Create a new key in the dashboard and send it in the Authorization: Bearer header.AuthenticationError
api_key_revoked403The API key was revoked.Create a new key in the dashboard and send it in the Authorization: Bearer header.ForbiddenError
project_disabled403The developer project that owns this key is disabled.Re-enable the project in the dashboard, or use a key from an active project.ForbiddenError
api_access_not_enabled403The account's plan does not include API access and it has no purchased credits.Upgrade to Growth, Pro or Enterprise, or buy credits; GET /usage shows the current balance.ForbiddenError
insufficient_scope403The key lacks the scope this call needs (transcriptions:read or transcriptions:write).Create a key with the required scope in the dashboard.ForbiddenError
rate_limited429The key exceeded its per-minute or daily request limit.Wait the number of seconds given in the Retry-After header before retrying, and spread requests over time.RateLimitError
invalid_stream_token401The stream token is invalid, expired, or was already used.Mint a fresh token with POST /auth/stream-token immediately before connecting — tokens are single-use and short-lived.StreamTokenError
stream_token_kind_mismatch403The stream token was minted for the other kind of stream.Mint with POST /auth/stream-token with type: "asr" for /transcribe/stream and type: "tts" for /tts/stream.StreamTokenError

Request validation

CodeStatusMeaningWhat to doSDK exception
invalid_json400The request body was not valid JSON.The SDK always sends JSON — if you see this, report it with the requestId.ValidationError
invalid_json_shape400The request body must be a JSON object.The SDK always sends an object — if you see this, report it with the requestId.ValidationError
invalid_payload400One or more request fields failed validation.Inspect issues for the offending fields.ValidationError
invalid_query400One or more query parameters failed validation.Inspect issues for the offending parameters.ValidationError
invalid_cursor400The pagination cursor could not be decoded.Pass the next_cursor value from the previous GET /transcriptions page unchanged.ValidationError
invalid_date_range400start_date must be before end_date.Check the dates passed to GET /usage/breakdown.ValidationError
invalid_multipart_body400The multipart upload could not be parsed.Retry POST /transcribe (multipart); if it persists, report it with the requestId.ValidationError
invalid_multipart_payload400The multipart form fields failed validation.Inspect issues for the offending fields.ValidationError
missing_audio400The upload contained no audio file.Pass the path of an existing audio or video file to POST /transcribe (multipart).ValidationError
unsupported_content_type415The request Content-Type is not one the endpoint accepts.Use POST /transcribe for URLs and POST /transcribe (multipart) for local files.UnsupportedMediaTypeError
voice_id_required400Every text-to-speech request needs a voice.Pick an id from GET /tts/voices and pass it as voice_id to POST /tts/generate or voiceId to /tts/stream.ValidationError
unsupported_language400The language is not in the platform's language catalog.Call GET /tts/languages for the codes currently enabled.UnsupportedLanguageError
streaming_unsupported_language400The language is served by batch synthesis only.Use POST /tts/generate for this language, or pick one with streaming_supported: true from GET /tts/languages.UnsupportedLanguageError

Transcription input

CodeStatusMeaningWhat to doSDK exception
empty_file400The uploaded file is empty.Check the file path passed to POST /transcribe (multipart).ValidationError
invalid_file_url400The audio URL is not a valid URL.Pass an absolute http(s) URL to POST /transcribe.ValidationError
unsupported_file_url_protocol400Only http and https URLs are accepted.Host the file on an http(s) URL and pass that to POST /transcribe.ValidationError
file_url_credentials_not_allowed400The audio URL must not embed a username or password.Use a public or pre-signed URL instead.ValidationError
file_url_unreachable422The server could not reach the audio URL.Make sure the URL is reachable from the public internet (not localhost or a private network).ValidationError
file_url_fetch_failed422The audio URL did not return a usable file.Verify the URL returns the raw media file with a 2xx response.ValidationError
empty_remote_file422The audio URL returned an empty file.Verify the URL serves the media file.ValidationError
file_too_large413The file exceeds the endpoint's hard upload limit.Trim or compress the audio before submitting.FileSizeLimitError
file_size_limit_exceeded413The file exceeds your plan's file-size limit.Compress the audio, split it, or upgrade your plan.FileSizeLimitError
unsupported_media415The file could not be read as audio or video.Submit a standard format such as mp3, wav, m4a, mp4 or webm.UnsupportedMediaTypeError
invalid_media_metadata415The media file could not be inspected safely.Re-encode the file into a standard format such as mp3, wav, m4a, mp4 or webm.UnsupportedMediaTypeError
missing_audio_stream415The file has no audio track.Submit a file that contains audio.UnsupportedMediaTypeError
unsupported_media_type415The media format is not supported.Submit a standard format such as mp3, wav, m4a, mp4 or webm.UnsupportedMediaTypeError
file_inspection_unavailable503Media inspection is temporarily unavailable on the server.Retry shortly.ServiceUnavailableError
file_storage_unavailable503Media storage is temporarily unavailable on the server.Retry shortly.ServiceUnavailableError
ingestion_failed500The server could not process the submitted audio.Retry; if it persists, report it with the requestId.ServerError

Voices

CodeStatusMeaningWhat to doSDK exception
unknown_voice_id422The voice id does not match an enabled preset or one of your custom voices.Use GET /tts/voices to see the available voices.UnknownVoiceError
voice_not_ready422The custom voice is still processing, failed, or is missing its reference audio.Wait for the voice to finish processing in the dashboard (GET /tts/voices only lists ready clones), or pick another voice.VoiceNotReadyError

Credits & limits

CodeStatusMeaningWhat to doSDK exception
insufficient_credits402Not enough purchased credits for this request — free credits cannot be spent through the API.Buy credits in the dashboard; GET /usage shows the current balance.InsufficientCreditsError
queue_limit_reached429Too many of your jobs are already waiting in the queue.Wait for queued jobs to start (poll the job endpoints) before submitting more.QueueLimitError
concurrency_limit_reached429Your plan's maximum number of processing jobs is already running.Retry after the number of seconds in Retry-After, or call POST /tts/generate with mode: "async" and poll GET /tts/{id}.ConcurrencyLimitError
stream_concurrency_limit429Your plan's concurrent-stream cap is already in use.Close an active stream (close an active stream) and reconnect.StreamConcurrencyError
stream_capacity503All streaming capacity is in use platform-wide.Retry shortly.ServiceUnavailableError

Availability

CodeStatusMeaningWhat to doSDK exception
tts_warming503The speech engine is cold-starting.Retry in about the number of seconds in Retry-After, or call POST /tts/generate with mode: "async" and poll GET /tts/{id}.ServiceWarmingError
streaming_unavailable503Real-time streaming is not configured on this deployment.Use the batch methods (POST /transcribe, POST /tts/generate) or contact support.ServiceUnavailableError
token_service_unavailable503The stream-token service is temporarily unavailable.Retry POST /auth/stream-token shortly.ServiceUnavailableError
websocket_required426The endpoint only accepts WebSocket connections.Use /transcribe/stream / /tts/stream, which connect over WebSocket.VoiceMakerAPIError

Processing

CodeStatusMeaningWhat to doSDK exception
synthesis_failed500The speech engine could not generate audio.Retry; if it persists, report it with the requestId.ServerError
audio_conversion_failed500The generated audio could not be converted to the requested output_format.Retry, or request output_format: "wav".ServerError
audio_upload_failed500The generated audio could not be stored.Retry; if it persists, report it with the requestId.ServerError
explain_failed500The explain service could not process the text.Retry; if it persists, report it with the requestId.ServerError
stream_setup_failed500The server could not initialise the streaming session.Retry; if it persists, report it with the requestId.ServerError
internal_error500An unexpected error occurred on the server.Retry; if it persists, report it with the requestId.ServerError
not_found404No job with that id exists for your project.Check the id — jobs are only visible to the project whose key created them.NotFoundError
endpoint_not_found404No API endpoint matches the request.Check baseUrl in the base URL — it must be the API origin with no path.NotFoundError

Any code this page does not list yet is still delivered in the same body shape; the SDKs map it by HTTP status and keep the raw code on err.code.

Streaming errors

The WebSocket endpoints (/transcribe/stream, /tts/stream) answer a refused connection with a plain HTTP error from the catalog above before the upgrade — 401, 402 (insufficient_credits for the reserve block), 403, 400/422 (voice_id_required, unknown_voice_id, voice_not_ready, unsupported_language, streaming_unsupported_language), 429 (stream_concurrency_limit), or 503 (tts_warming, stream_capacity, streaming_unavailable). Browsers hide that status from JavaScript, so the JavaScript SDK emits an error event with code upgrade_rejected and the likely causes; the Python SDK reads the body from the rejected handshake and raises the same typed exception a REST call would.

Once a session is open, errors arrive as { "type": "error", "code", "message" } frames. The session may continue afterwards or be closing, as noted:

CodeMeaningWhat to do
invalid_messageThe server received a control frame it could not parse or does not accept in the current state.Only use the connection's methods (sendAudio()/sendText(), finalize(), close()); stop sending after close().
text_too_largeA single sendText() call exceeded the per-frame character limit.Split the text with chunkText() and call sendText() per chunk.
session_char_limitThe session's total character budget is exhausted; audio already submitted is still delivered.Open a new /tts/stream session for the remaining text.
insufficient_creditsPurchased credits ran out mid-session; the server delivers what was already paid for, then closes (code 4002).Buy credits in the dashboard; GET /usage shows the current balance.
idle_timeoutNo audio/text or KeepAlive was received for the idle window; the session closes (code 4008).Keep the default keepAliveMs, or send data at least every 30 seconds.
upstream_unavailableThe speech engine behind the stream failed or is at capacity; the session closes (code 1011 or 1013).Reconnect after a short delay.
internal_errorAn unexpected server-side error ended the session (code 1011).Reconnect; if it persists, report it with the session_id.

Close codes

CodeMeaning
1000Session ended normally.
1006Connection dropped without a close frame — the upgrade was rejected, the network failed, or the server went away.
1011The speech engine or the server hit an internal error.
1013The speech engine is at capacity; reconnect after a short delay.
4002Purchased credits were exhausted.
4008Idle timeout — no data or KeepAlive for 30 seconds.
4009The session reached its maximum duration or character budget.

SDK-side errors

The SDKs also raise errors of their own before or instead of a request. They share the code field with API errors:

CodeRaised by the SDK whenException
api_key_requiredThis call needs an API key and the client was created without one.ConfigurationError
credential_requiredStreaming needs an API key or an ephemeral stream token.ConfigurationError
websocket_unavailableThis runtime has no global WebSocket implementation.ConfigurationError
invalid_argumentAn argument failed client-side validation before any request was made.InvalidArgumentError
stream_not_openData was sent on a stream that is not open.StreamClosedError
network_errorThe request never reached the API (DNS, connection refused, TLS, CORS, offline).NetworkError
request_timeoutThe API did not answer within the client's timeout.RequestTimeoutError
poll_timeoutThe job did not reach a terminal status before the polling deadline.PollTimeoutError

Common error scenarios

Voice required (400)

Every text-to-speech request needs a voice_id (batch POST /dev/v1/tts/generate and the /dev/v1/tts/stream WebSocket alike). A missing or blank voice_id is refused before anything else is validated:

JSON
{
  "error": "Voice required",
  "detail": "Specify a voice_id.",
  "errorCode": "voice_id_required",
  "requestId": "..."
}

Pick an ID from GET /dev/v1/tts/voices.

Unknown voice (422)

JSON
{
  "error": "Unknown voice",
  "detail": "voice_id 'foo' does not match an enabled voice preset or one of your custom voices. Use GET /dev/v1/tts/voices for the list of available voices.",
  "errorCode": "unknown_voice_id",
  "requestId": "..."
}

Voice not ready (422)

The voice_id matched one of your custom cloned voices, but the voice cannot synthesize yet (still processing, failed, or missing its reference assets).

JSON
{
  "error": "Voice not ready",
  "detail": "Custom voice 'My Studio Clone' is not ready for synthesis (status: PROCESSING)",
  "errorCode": "voice_not_ready",
  "requestId": "..."
}

Insufficient credits (402)

JSON
{
  "error": "Insufficient credits",
  "detail": "Not enough purchased credits. Required: 1,500 credits.",
  "errorCode": "insufficient_credits",
  "requestId": "..."
}

File too large (413)

JSON
{
  "error": "File too large",
  "detail": "Audio file exceeds the 25MB limit for your current plan.",
  "errorCode": "file_size_limit_exceeded",
  "requestId": "..."
}

Engine warming up (503)

Synchronous TTS on a cold engine answers 503 with Retry-After: 60. Retry after that, or submit with mode: "async" and poll GET /dev/v1/tts/{id}.

JSON
{
  "error": "TTS engine warming up",
  "detail": "The TTS engine is starting up (cold start). Retry in ~1 minute, or submit with mode:\"async\" to queue the job and poll GET /dev/v1/tts/:id.",
  "errorCode": "tts_warming",
  "requestId": "..."
}

Rate limited (429)

JSON
{
  "error": "Rate limit exceeded",
  "detail": "This developer API key has exceeded its current rate limit",
  "errorCode": "rate_limited",
  "requestId": "..."
}

Inspect the Retry-After header to learn how long to wait before retrying.

Handling errors in the SDKs

Both SDKs raise typed exceptions whose message names the SDK method that failed, rewrites endpoint references into SDK calls, and ends with the code and request id:

Code
tts.generate(): Unknown voice — voice_id 'foo' does not match an enabled voice
preset or one of your custom voices. Use tts.listVoices() for the list of
available voices. [unknown_voice_id · request 8f1c2d3e]
TypeScript
import { UnknownVoiceError, ServiceWarmingError, VoiceMakerAPIError } from "myvoicemaker";

try {
  await client.tts.generate({ text, voice_id });
} catch (err) {
  if (err instanceof UnknownVoiceError) { /* pick from client.tts.listVoices() */ }
  else if (err instanceof ServiceWarmingError) { /* retry in err.retryAfter s, or mode: "async" */ }
  else if (err instanceof VoiceMakerAPIError) { console.error(err.code, err.requestId, err.hint); }
}
Python
from myvoicemaker import UnknownVoiceError, ServiceWarmingError, VoiceMakerAPIError

try:
    client.tts.generate(text, voice_id=voice_id)
except UnknownVoiceError:
    ...  # pick from client.tts.list_voices()
except ServiceWarmingError as e:
    ...  # retry in e.retry_after s, or mode="async"
except VoiceMakerAPIError as e:
    print(e.code, e.request_id, e.hint)

Every SDK error exposes code, method, hint, description, retryable, retryAfter/retry_after, requestId/request_id and issues; the full class trees are in each SDK's README.