Troubleshooting
Troubleshooting
Section titled “Troubleshooting”Common failure modes, what they mean, and how to fix them. Every API failure returns a single JSON object with a stable, machine-readable error code (for example { "error": "worker_unavailable" }) — branch on that code and the HTTP status.
A job sits at queued and the console says “warming up”
Section titled “A job sits at queued and the console says “warming up””What it means: nothing is wrong. The synthesis engine scales down to nothing when idle, and your request arrived while it was cold. The job is safely queued and starts as soon as the engine is ready.
Fix: wait. Warm-up can take several minutes.
- Your call returned
202with"status": "queued"— including on thesync: truepath, which degrades to polling rather than failing. - Poll
GET /v1/jobs/{id}rather than resubmitting. Resubmitting just queues a second clip and spends your character allowance twice. If you must retry safely, send anIdempotency-Keyand the replay returns the original job.
503 worker_unavailable
Section titled “503 worker_unavailable”What it means: no synthesis engine is configured at all — not busy, not cold.
This is deliberately loud. Text-to-Speech will not quietly substitute a different engine and hand you audio in a voice you didn’t ask for.
Fix: on hosted Destesi this is an incident — report it with the request id. Self-hosters: no engine is wired at all.
400 unsupported_parameter
Section titled “400 unsupported_parameter”What it means: you sent instruct where it does not apply — to a voice whose capabilities don’t list it, or on a request routed at an external provider that has no such parameter. The control is a property of the voice, and the API refuses rather than dropping the parameter silently — a control that is sometimes applied and sometimes ignored is worse than one that says no.
Fix: read the voice’s capabilities from GET /v1/voices and either drop the control or choose a voice that supports it.
Related: 400 invalid_instruct (over 500 characters).
409 idempotency_conflict
Section titled “409 idempotency_conflict”What it means: you reused an Idempotency-Key with a different request body. The key is already bound to another request, and Text-to-Speech won’t guess which one you meant.
Fix: use a fresh key for a genuinely new request, or resend the original body byte-for-byte to get the original job back.
429 — you’re at your plan allowance
Section titled “429 — you’re at your plan allowance”What it means: the workspace has used its measured allowance of characters synthesized this month.
Fix: check GET /v1/stats to see where you stand, or move to a plan with a larger allowance.
409 not_connected or 502 provider_error
Section titled “409 not_connected or 502 provider_error”What it means: you routed synthesis at an external provider. not_connected means this workspace has no credential for it; provider_error means the provider itself failed.
Fix: connect the provider in Connect, or drop the provider choice and use a built-in voice. A provider_error is upstream — retry, and report it with the request id if it persists.
404 voice_not_found
Section titled “404 voice_not_found”What it means: the voice_id is not visible to the workspace this request runs in.
Fix:
- Confirm the ID exists with
GET /v1/voicesfor the current workspace. - If you authenticate with a token, check the
X-Destesi-Workspaceheader points at the workspace you meant — one token spans every workspace you belong to, so it is easy to call the wrong one.
The audio is silent or clipped
Section titled “The audio is silent or clipped”What it means: a clip plays but contains no (or barely any) sound.
Fix: treat it as an engine issue and retry the job. If it repeats, report it with the request id from the X-Request-Id response header.
Authentication failures
Section titled “Authentication failures”error |
Cause | Fix |
|---|---|---|
missing_session |
No credentials were supplied. | Sign in again, or add an Authorization: Bearer idn_pat_… header. |
session_invalid |
The browser session was rejected (signed out or expired). | Sign in again at account.destesi.io. |
unknown_bearer_scheme |
The Bearer value is not an idn_pat_ token. |
Use a Personal Access Token from account.destesi.io/settings/api-tokens. The header does not fall back to a cookie. |
missing_workspace_header |
A token was sent without X-Destesi-Workspace. |
Add the header with your workspace slug. |
invalid_token |
The token was rejected by accounts (revoked, expired, or not valid for that workspace). | Mint a fresh token, and confirm X-Destesi-Workspace names a workspace you belong to. |
A revoked token fails on the very next call — Text-to-Speech re-validates against the central accounts service on every request, so there is no cached-credential window. Mint a fresh token if yours stopped working. See API access & the dst CLI.
A previously working audio_url now returns an error
Section titled “A previously working audio_url now returns an error”What it means: signed audio URLs expire after one hour. After that the link is dead.
Fix: call GET /v1/jobs/{id}/audio to get a fresh redirect, or fetch the clip from your Drive library, where every finished job is permanently registered.
404 from GET /v1/jobs/{id}/audio
Section titled “404 from GET /v1/jobs/{id}/audio”What it means: the job is not done, or it never produced audio (for example, it failed).
Fix: check the job state first with GET /v1/jobs/{id}. Only done jobs have downloadable audio.
400 text_too_long
Section titled “400 text_too_long”What it means: the request text exceeds the 5,000-character limit.
Fix: split the text into chunks under 5,000 characters, synthesize each, and concatenate the resulting WAVs. For anything over 500 characters, also use sync: false and poll the job rather than blocking inline.
Can’t reach the API at all
Section titled “Can’t reach the API at all”- Confirm you are calling
https://api.tts.destesi.io(the API host), nothttps://tts.destesi.io(the web console). - The
GET /healthzandGET /readyzprobes need no auth and returnok— use them to confirm the service is up before debugging your request.