> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting: transcription, synthesis, and deepfake failures

> Resolve STT/TTS failures, missing voice and video transcripts, malformed LLM summaries, and the DEEPFAKE_DETECTED synthetic-voice rejection.

# Troubleshooting: transcription, synthesis, and deepfake failures

This page owns the failure codes for every produce-then-read pipeline that
turns speech into text or text into speech: the STT (speech-to-text) step,
the TTS (text-to-speech) step, the voice and video transcript surfaces, the
AI summary/eval steps running an LLM over those transcripts, and the
synthetic-voice classifier on voice-biometrics enrollment. Each code tells
you which stage broke, and the stage tells you who can fix it — the
produce-side codes point at upstream provider health, the read-side codes
point at a retry-safe surface failure.

Read `error.code` off the envelope; the matrix below maps each code to the
stage that raised it and to the section with the cause table, envelope
sample, and ticket checklist.

## Code matrix — symptom to section

| Error code                          | HTTP | Stage that raised it                                                  | Section                                                                                    |
| ----------------------------------- | ---- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `STT_TRANSCRIPTION_FAILED`          | 500  | STT provider (Deepgram primary, failover chain in the agent pipeline) | [STT — upstream and codec](#stt--upstream-and-codec)                                       |
| `TTS_SYNTHESIS_FAILED`              | 500  | TTS provider (Cartesia primary, ElevenLabs fallback when configured)  | [TTS — primary and fallback](#tts--primary-and-fallback)                                   |
| `CALL_TRANSCRIPT_UNAVAILABLE`       | 422  | Voice summary pre-check: no usable transcript on the call yet         | [Transcript — not yet produced](#transcript--not-yet-produced)                             |
| `VIDEO_LIVE_TRANSCRIPT_UNAVAILABLE` | 422  | Live video ask pre-check: no usable captions yet                      | [Transcript — not yet produced](#transcript--not-yet-produced)                             |
| `VIDEO_TRANSCRIPT_UNAVAILABLE`      | 422  | Video summary pre-check: no usable transcript on the meeting          | [Transcript — not yet produced](#transcript--not-yet-produced)                             |
| `VIDEO_TRANSCRIPT_LIST_FAILED`      | 500  | Video transcript read surface                                         | [Transcript reads and exports — retry-safe](#transcript-reads-and-exports--retry-safe)     |
| `VIDEO_TRANSCRIPT_EXPORT_FAILED`    | 500  | Video transcript export surface                                       | [Transcript reads and exports — retry-safe](#transcript-reads-and-exports--retry-safe)     |
| `VIDEO_TRANSCRIPT_CORRECT_FAILED`   | 500  | Caption-correction write surface                                      | [Transcript reads and exports — retry-safe](#transcript-reads-and-exports--retry-safe)     |
| `DEEPFAKE_DETECTED`                 | 422  | Synthetic-voice classifier on biometrics enrollment                   | [DEEPFAKE\_DETECTED — classifier, not consent](#deepfake_detected--classifier-not-consent) |
| `CALL_SUMMARY_LLM_MALFORMED`        | 502  | LLM reply that failed validation on a voice summary/eval              | [LLM summaries — malformed output](#llm-summaries--malformed-output)                       |
| `VIDEO_SUMMARY_LLM_MALFORMED`       | 502  | LLM reply that failed validation on a video summary                   | [LLM summaries — malformed output](#llm-summaries--malformed-output)                       |

## STT — upstream and codec

`STT_TRANSCRIPTION_FAILED` (500) means the speech-to-text provider either
did not answer or returned an unusable result, on the standalone
`POST /api/v1/stt/transcribe` surface or inside an agent pipeline turn.
Orbit routes STT through Deeway primary (the provider named in the
[FAQ's pipeline](/reference/faq)) with a failover chain the agent layer
re-orders per caller-requested language; a 500 on this surface fires only
when every leg came back empty or errored.

Codec matters here for standalone uploads: when the source call negotiates
a transcoding-heavy codec, the gateway funnels PCMU/PCMA frames through a
decode buffer before STT; a misalignment at that boundary produces
untranscribable audio rather than a provider failure. Confirm the negotiated
codec on the call — OPUS, PCMU (G.711 µ-law), or PCMA (G.711 A-law) are
the supported set per the [FAQ codec list](/reference/faq) — and re-send a
clip in one of those before treating the failure as provider-side.

### Cause table

| Symptom                                                | Likely cause                                             | Fix                                                                                                   |
| ------------------------------------------------------ | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| Bursts of 500s across tenants, then clean              | Deepgram upstream wobble                                 | Retry with backoff; the window should close on its own                                                |
| Failures only on a specific `?language=` value         | The language-specific failover leg missing or misordered | Move that language off the affected leg or drop the language pin until provider alignment is restored |
| Fails on uploaded file while live-call STT stays clean | Unsupported codec or corrupt upload                      | Re-send the clip as OPUS / PCMU / PCMA                                                                |
| Persistent 500s across every caller and codec          | Provider chain exhausted                                 | Escalate with the samples below                                                                       |

### Retry-safety

Safe to retry — a transcribe call writes nothing before the provider
answers (the audio upload cap is enforced before the provider hop). Use
exponential backoff over tens of seconds; a tight loop spends your rate
limit against the same outage.

### Envelope sample

```json theme={null}
{
  "error": {
    "code": "STT_TRANSCRIPTION_FAILED",
    "message": "Speech-to-text transcription failed",
    "status": 500
  },
  "meta": {
    "request_id": "req_stt_fail1",
    "timestamp": "2026-09-13T08:12:44Z"
  }
}
```

### Ticket checklist

1. The **code** (`STT_TRANSCRIPTION_FAILED`) and the endpoint you called
   (`/api/v1/stt/transcribe`).
2. The **codec** the source call negotiated (OPUS / PCMU / PCMA) or the
   upload format you passed.
3. Request IDs and timestamps for two or three failing calls — that
   brackets the provider window.
4. Your **organization ID** (Settings → Organization, or
   `organizationId` from `GET /api/v1/me`).

## TTS — primary and fallback

`TTS_SYNTHESIS_FAILED` (500) fires on `POST /api/v1/tts/synthesize` (and
on the agent-turn path) when synthesis failed on every provider leg. The
[FAQ's STT → LLM → TTS pipeline](/reference/faq) routes TTS through
Cartesia as the primary provider; the agent pipeline falls to an
ElevenLabs leg when one is configured, with a per-leg timeout budget set
in milliseconds rather than a blocking wait. A 500 means both legs failed
or no fallback was configured.

Voice matters most here: the most common tenant-side cause is a
`voice_id` the provider rejects — a passed id that no longer exists on the
configured provider, or an agent whose saved voice config points at a
provider the organization has not activated. Retry once on the same id only
after re-reading the agent's voice configuration; otherwise verify the
configured primary endpoint and the fallback leg before retrying again.

### Cause table

| Symptom                                                  | Likely cause                                                     | Fix                                                           |
| -------------------------------------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------- |
| 500 with a specific `voice_id` value                     | Provider rejected the id (stale, or on an inactive provider leg) | Verify the agent's voice config, or drop the pin to `default` |
| Fails while the failover leg stays clean on other agents | Primary leg timeouts during load                                 | Escalate with the triage split below                          |
| Persistent 500s, every caller, every voice id            | Provider chain exhausted                                         | Escalate with the samples below                               |

### Retry-safety

Safe to retry — synthesis writes nothing before the provider answers. The
one non-retry case is the bad `voice_id`: a retry against the same
rejected id fails identically, so fix the voice config first.

### Envelope sample

```json theme={null}
{
  "error": {
    "code": "TTS_SYNTHESIS_FAILED",
    "message": "Text-to-speech synthesis failed",
    "status": 500
  },
  "meta": {
    "request_id": "req_tts_fail1",
    "timestamp": "2026-09-13T08:37:12Z"
  }
}
```

### Ticket checklist

1. The **code** (`TTS_SYNTHESIS_FAILED`) and the endpoint
   (`/api/v1/tts/synthesize`, or the agent-turn path it rode on).
2. The **voice id** you passed (or `default` when omitted).
3. Whether the failure is **primary-leg-only** or all-legs — the split
   decides between provider drift and chain exhaustion.
4. Request IDs and timestamps for failing calls, plus your **organization
   ID**.

## Transcript — not yet produced

All three codes here are 422 *pre-check* verdicts, not failures: the
summary (or live-ask) service looked for a usable transcript, found none,
and refused. That is distinct from the 5xx read/write failures in the
next section — those are transient server-side faults you retry; a 422
here is a readiness state you resolve by waiting or by enabling capture.

* **`CALL_TRANSCRIPT_UNAVAILABLE`** fires on
  `POST /api/v1/voice/calls/:id/summary` when the call has no usable
  transcript yet — typically because the recording→transcription producer
  has not finished, or transcription was disabled on the call.
* **`VIDEO_LIVE_TRANSCRIPT_UNAVAILABLE`** fires on the in-meeting
  live-assist endpoint
  (`POST /api/v1/video/rooms-scheduled/:id/ask`) when the room has
  captured no captions yet — captions off, or too little speech so far.
* **`VIDEO_TRANSCRIPT_UNAVAILABLE`** fires on
  `POST /api/v1/video/sessions/:id/summary` when the meeting has no usable
  transcript — the meeting is still live, or closed captions were never
  enabled.

### Cause table

| Symptom                                            | Likely cause                                         | Fix                                                                         |
| -------------------------------------------------- | ---------------------------------------------------- | --------------------------------------------------------------------------- |
| 422 immediately after the call / meeting ended     | Producer still running                               | Retry after a short settle; the summary pre-check is deliberately fail-fast |
| 422 on a live meeting ask                          | Captions disabled, or the room saw too little speech | Turn captions on, or retry after the meeting has spoken enough content      |
| 422 with a transcript visible on the call timeline | Transcript too short for a useful summary            | Pass an explicit `transcript_override` on the request body                  |

For voice calls whose transcript never lands at all, check the
production side (recording/transcription enablement on the call flow)
before re-issuing the summary; for video, confirm the room had
captions/transcription enabled before the call.

### Envelope sample

```json theme={null}
{
  "error": {
    "code": "CALL_TRANSCRIPT_UNAVAILABLE",
    "message": "No usable transcript is available on this call yet",
    "status": 422
  },
  "meta": {
    "request_id": "req_call_sum_tr1",
    "timestamp": "2026-09-13T10:02:19Z"
  }
}
```

### Ticket checklist

Only escalate when the transcript probe has been re-checked after the
producer finished:

1. The **code** (one of the three above) and the endpoint.
2. The **call id / session\_id / room id** of the missing transcript.
3. Whether **captions or transcription were enabled** on that call or
   room, and how long since it ended.
4. Your **organization ID**.

## Transcript reads and exports — retry-safe

The hosted video transcript surfaces read the captured record a meeting
(or the correction write) produced. The three 5xx codes below mean that
read/write hit a transient server fault — a tenant-schema blip, a DB
pool exhaustion moment, or an export-pipeline race — and the request
never partially applied anything. All three are safe to retry.

* **`VIDEO_TRANSCRIPT_LIST_FAILED`** (500) on
  `GET /api/v1/video/rooms-scheduled/:id/transcripts` — the paginated
  replay surface failed to load the transcript rows.
* **`VIDEO_TRANSCRIPT_EXPORT_FAILED`** (500) on
  `GET /api/v1/video/rooms-scheduled/:id/transcripts/export` — the
  caption-file export (WebVTT/SRT/digest) failed.
* **`VIDEO_TRANSCRIPT_CORRECT_FAILED`** (500) on the caption-correction
  surface (`PATCH /api/v1/video/rooms-scheduled/:id/transcripts/:transcriptId` or
  the POST-correction variant on the same room) — the write failed.

This split is the diagnostic the producer-versus-read ladder gives you:
a 422 from the previous section is a produce-side readiness verdict (wait
or enable captions), a 5xx here is a read-side fault (retry, then escalate
if it persists — the pure-read path (`LIST_FAILED`, `UNAVAILABLE`) never
touches the write path (`CORRECT_FAILED`, `EXPORT_FAILED`).

### Cause table

| Symptom                                        | Likely cause                                    | Fix                                                      |
| ---------------------------------------------- | ----------------------------------------------- | -------------------------------------------------------- |
| Bursts of 500s across rooms, then clean        | Transient DB / export-pipeline condition        | Retry with backoff                                       |
| Fails on export while list and read stay green | Export pipeline (caption-file render) condition | Retry export only; persistent failures are platform-side |
| Persistent 500s on the read surface            | Tenant-schema or pool condition                 | Escalate with the samples below                          |

### Envelope sample

```json theme={null}
{
  "error": {
    "code": "VIDEO_TRANSCRIPT_LIST_FAILED",
    "message": "We couldn't load the transcript for this room. Please try again.",
    "status": 500
  },
  "meta": {
    "request_id": "req_vid_tr_list1",
    "timestamp": "2026-09-13T11:14:03Z"
  }
}
```

### Ticket checklist

1. The **code** (one of the three above) and the endpoint.
2. The **room id** you were reading (or the `transcriptId` on a
   correction write).
3. Whether **every** transcript surface fails or only one — the split
   separates a room-scoped condition from a surface-scoped one.
4. Your **organization ID**.

## DEEPFAKE\_DETECTED — classifier, not consent

`DEEPFAKE_DETECTED` (422) is the synthetic-voice family classifier on the
voice-biometrics enrollment surfaces: the Resemble Detect synthetic-speech
score on the sample you submitted met or exceeded the platform threshold,
so the enrollment (or the clone-create request) refused. It is a classifier
verdict on the media you handed us — not a rights problem, not a consent
problem.

Distinguish it from the consent gates deliberately, because the two
families interact. The consent family
([AI voice consent guard](/compliance/fcc-ai-voice-consent-guard),
`FCC_AI_VOICE_WRITTEN_CONSENT_REQUIRED` on the consent-check surfaces
before dial) is a recipient-consent record check — a written consent
record on the recipient must grant outbound synthetic voice content. The
deepfake classifier here is a media-authenticity check — the audio sample
itself was flagged as synthetic or cloned. Pasting a full-consent URL on
an enrollment the classifier refused still returns the same 422; the fix
is a genuine human-speech sample, not another consent record.

For the provenance side — how AI-generated output carries content
credentials so recipients and reviewers can check a provenance claim — see
[Synthetic-media provenance](/concepts/synthetic-media-provenance). The
consent gates and the deepfake classifier both sit under the synthetic-voice
family umbrella, but this error carries only the classifier verdict: the
sample did not authenticate as human-performable speech. Allowed voice
clones enroll through the consent-registered speech
([create a voice clone](/guides/voice-clones)) before any watermark or
synthesis runs, and the classifier applies to the enrollment sample itself.

### Cause table

| Symptom                                         | Likely cause                                            | Fix                                                                        |
| ----------------------------------------------- | ------------------------------------------------------- | -------------------------------------------------------------------------- |
| Enrollment rejected on a TTS-synthesized sample | Classifier working as designed                          | Submit a human-spoken sample                                               |
| Clone-create rejected on a repeated TTS sample  | Classifier over-threshold persists                      | Re-record from a human speaker; a consent record does not lift the verdict |
| Sibling `SPOOF_DETECTED` verdict instead        | Anti-spoof score fell below threshold on a human sample | Improve the sample (stationary mic, less noise); still human-speech-only   |

### Retry-safety

Do not retry in a loop — every retry with the same flagged sample fails
identically. Resolve the sample first, then re-submit once.

### Envelope sample

```json theme={null}
{
  "error": {
    "code": "DEEPFAKE_DETECTED",
    "message": "Voice enrollment rejected — Resemble Detect flagged the audio as a synthetic / cloned voice (deepfake score at or above threshold).",
    "status": 422
  },
  "meta": {
    "request_id": "req_df_detect1",
    "timestamp": "2026-09-13T09:21:55Z"
  }
}
```

### Ticket checklist

1. The **code** (`DEEPFAKE_DETECTED`) and the enrollment endpoint.
2. A short **sample-description** for the audio (recording method,
   speaker) — confirm it is a genuine human sample, not TTS.
3. Whether the rejection happens **on enrollment** or on
   **clone-create** — the split separates classifier verdicts from
   clone-create pathing.
4. Your **organization ID**. Consent gates are a separate family; only
   include a consent record reference if you have already verified it.

## LLM summaries — malformed output

`CALL_SUMMARY_LLM_MALFORMED` (502) and `VIDEO_SUMMARY_LLM_MALFORMED` (502)
fire when the LLM endpoint answered, but its reply failed validation — the
summary service could not parse the expected JSON shape from the model
output, after retries at the service layer. The prompt was fine; the
model's output was not.

Model drift under provider load is the usual cause, and the failure is
safe to retry: the request writes nothing before the summary is accepted,
and a retry rides the same transcript probe. A model endpoint that has
recovered answers the retry cleanly. When the retries you issued keep
returning the same 502, treat that window as a provider incident and
escalate — with the request IDs you captured, the recovery is minutes
rather than hours.

The follow-on watch item is the 503 sibling `LLM_PROVIDER_UNCONFIGURED` —
a deployment with no LLM key configured does not reach the malformed
stage; the summary surface raises the unconfigured code before any model
call. Escalate immediately there rather than retrying.

### Cause table

| Symptom                                                     | Likely cause                       | Fix                                                                                                  |
| ----------------------------------------------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Sporadic 502s on summary requests                           | Model drift under provider load    | Retry; the recovered endpoint answers cleanly                                                        |
| Persistent 502s on one modality (voice only, or video only) | Surface-scoped validation mismatch | Escalate — the voice/video summary paths emit distinct codes so the per-surface rate is identifiable |
| Consistent 503 with `LLM_PROVIDER_UNCONFIGURED`             | No LLM key configured              | Escalate — nothing tenant-side fixes this                                                            |

### Retry-safety

Safe to retry. Service-layer retries are exhausted before the 502 is
surfaced, but a bounded manual backoff (three attempts over tens of
seconds) is the right cadence; a tight loop spends your rate limit against
the same malformed output.

### Envelope sample

```json theme={null}
{
  "error": {
    "code": "CALL_SUMMARY_LLM_MALFORMED",
    "message": "The summary response from the language model was malformed after retries.",
    "status": 502
  },
  "meta": {
    "request_id": "req_sum_malformed1",
    "timestamp": "2026-09-13T12:48:31Z"
  }
}
```

### Ticket checklist

1. The **code** (`CALL_SUMMARY_LLM_MALFORMED` or
   `VIDEO_SUMMARY_LLM_MALFORMED`) and the endpoint.
2. Request IDs and timestamps for the failing calls — that brackets the
   provider window.
3. The **call id / session id** you were summarizing.
4. Your **organization ID**.

## Do NOT retry these

* **Do not retry `DEEPFAKE_DETECTED` in a loop.** Every retry signs the
  same flagged sample over the classifier and fails identically; only a
  new genuine sample changes the verdict.
* **Do not submit longer samples to beat STT/TTS timeouts.** A rejected
  `voice_id` or an unsupported codec is deterministic — a retry with the
  same payload reads identically on every attempt.
* **Do not treat a 5xx transcript read as a transcript-absence verdict.**
  A `VIDEO_TRANSCRIPT_LIST_FAILED` or `CORRECT_FAILED`/`EXPORT_FAILED`
  is a surface fault — retry it before you escalate; only the 422 family
  is a produce-side readiness verdict.
* **Do not re-synthesize the same `voice_id`.** A rejected voice id is
  deterministic; fix the agent's voice config before re-issuing the call.

## See also

* [Error codes reference](/reference/error-codes) — the full catalog these
  codes live in, with HTTP status and retry-safety class.
* [Frequently asked questions](/reference/faq) — the STT → LLM → TTS
  pipeline and the codec list on the voice lane.
* [Synthetic-media provenance](/concepts/synthetic-media-provenance) —
  how AI-generated voice and video output carries a tamper-evident
  credential.
* [AI voice consent guard](/compliance/fcc-ai-voice-consent-guard) — the
  consent family the deepfake classifier is deliberately distinct from.
* [Voice biometrics failures](/troubleshooting/voice-biometrics-failures) —
  the sibling sidecar connectivity and auth runbook on the same
  enrollment surfaces.
* [Troubleshooting hub](/reference/troubleshooting-hub) — the runbook
  index this page registers under.
