Mintplex-Labs/anything-llm · error · Error

Deepgram transcription failed

Error message

Deepgram transcription failed (${response.status}) - ${errBody}

What it means

DeepgramSTT POSTs the raw audio buffer to https://api.deepgram.com/v1/listen with an Authorization: Token header; when the response status is not ok, it reads the body as text and throws this templated error embedding the HTTP status and Deepgram's error body. The status code identifies the failure class (401 auth, 400 bad request/model, 413 payload, 429 rate limit, 5xx upstream).

Solutions

  1. Match the embedded status: 401/403 → fix STT_DEEPGRAM_API_KEY; 400 → check model name and audio format; 429 → slow down or upgrade the Deepgram plan; 5xx → retry shortly
  2. Read the errBody fragment in the message — Deepgram states the exact reason (e.g. invalid model, malformed audio)
  3. Verify STT_DEEPGRAM_MODEL is a currently valid model (default nova-3)
  4. For very long recordings, split the audio into smaller chunks before sending

Example fix

# model typo producing 400s
# before (.env)
STT_DEEPGRAM_MODEL=nova-2-general

# after (.env)
STT_DEEPGRAM_MODEL=nova-3
Defensive patterns

Strategy: retry

Validate before calling

// Nothing to pre-validate beyond the key; assert it to fail early on the auth class
if (!process.env.STT_DEEPGRAM_API_KEY) throw new Error('Missing STT_DEEPGRAM_API_KEY');

Try / catch

const RETRYABLE = new Set([408, 429, 500, 502, 503, 504]);
function parseDeepgramStatus(err) {
  const m = err.message.match(/Deepgram transcription failed \((\d+)\)/);
  return m ? Number(m[1]) : null;
}
for (let attempt = 0; attempt < 3; attempt++) {
  try { return await stt.process(audioBuffer, filename); }
  catch (err) {
    const status = parseDeepgramStatus(err);
    if (status && RETRYABLE.has(status)) { await sleep(2 ** attempt * 500); continue; }
    throw err; // 401/400/413 are permanent — surface them
  }
}

Prevention

When it happens

Trigger: 401/403 with an invalid or revoked STT_DEEPGRAM_API_KEY; 400 when the Content-Type mapped from the file extension is wrong, the audio is corrupt, or STT_DEEPGRAM_MODEL names a nonexistent model; 413 for very large files; 429 when the key's rate/concurrency limit is hit.

Common situations: Expired or mis-pasted Deepgram key; uploading webm/ogg files whose extension mapping produces a Content-Type Deepgram rejects; setting STT_DEEPGRAM_MODEL to a deprecated or typo'd model name; bursting many transcriptions on a free-tier key.

Related errors


AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18). Data as JSON: /api/errors/f85aeaee4c1acc5d. Report an issue: GitHub.

Appendix: source

Thrown at server/utils/SpeechToText/deepgram/index.js:61

   * @returns {Promise<string>} The transcribed text.
   */
  async transcribe(audioBuffer, filename = "audio.webm") {
    const url = new URL(this.endpoint);
    url.searchParams.set("model", this.model);
    url.searchParams.set("smart_format", "true");

    return await fetch(url.toString(), {
      method: "POST",
      headers: {
        Authorization: `Token ${this.apiKey}`,
        "Content-Type": this.#contentTypeFromFilename(filename),
      },
      body: audioBuffer,
    })
      .then(async (response) => {
        if (!response.ok) {
          const errBody = await response.text().catch(() => "");
          throw new Error(
            `Deepgram transcription failed (${response.status}) - ${errBody}`
          );
        }
        return response.json();
      })
      .then((result) => {
        return (
          result?.results?.channels?.[0]?.alternatives?.[0]?.transcript ?? ""
        );
      })
      .catch((error) => {
        this.#log(`Deepgram transcription failed - ${error.message}`);
        throw new Error(`Deepgram transcription failed - ${error.message}`);
      });
  }
}

module.exports = { DeepgramSTT };

View on GitHub (pinned to 3aec848f28)