zeroclaw-labs/zeroclaw · error

Deepgram API error ({}): {}

Error message

Deepgram API error ({}): {}

What it means

DeepgramProvider::transcribe() POSTs the audio to the Deepgram API and bails when the response status is not 2xx, embedding the HTTP status plus the 'err_msg' or 'error' field from Deepgram's JSON body. This is Deepgram's own error surfacing — the request reached Deepgram and was rejected. The provider was already constructed successfully, so registration/config shape was fine; the failure is authentication, payload, or quota.

Source

Thrown at crates/zeroclaw-channels/src/transcription.rs:415

            .header("Content-Type", mime)
            .body(audio_data.to_vec())
            .timeout(std::time::Duration::from_secs(TRANSCRIPTION_TIMEOUT_SECS))
            .send()
            .await
            .context("Failed to send transcription request to Deepgram")?;

        let status = resp.status();
        let body: serde_json::Value = resp
            .json()
            .await
            .context("Failed to parse Deepgram response")?;

        if !status.is_success() {
            let error_msg = body["err_msg"]
                .as_str()
                .or_else(|| body["error"].as_str())
                .unwrap_or("unknown error");
            bail!("Deepgram API error ({}): {}", status, error_msg);
        }

        let text = body["results"]["channels"][0]["alternatives"][0]["transcript"]
            .as_str()
            .context("Deepgram response missing transcript field")?
            .to_string();

        Ok(text)
    }
}

// ── AssemblyAiProvider ──────────────────────────────────────────

/// AssemblyAI STT API transcription_provider.
pub struct AssemblyAiProvider {
    alias: String,
    api_key: String,
}

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read {status}: 401/403 means the api_key in [transcription.deepgram] is wrong or revoked — update it
  2. 400 means Deepgram rejected the audio itself — verify the file extension matches the real encoding (re-encode with ffmpeg if needed)
  3. 402 means out of quota — check billing in the Deepgram console
  4. 429/5xx are transient — retry the transcription with backoff

Example fix

# before
[transcription.deepgram]
api_key = "expired-key-abc"

# after
[transcription.deepgram]
api_key = "valid-key-from-console"
Defensive patterns

Strategy: try-catch

Try / catch

match provider.transcribe(&audio, name).await {
    Ok(text) => Ok(text),
    Err(e) => {
        let msg = e.to_string();
        if msg.contains("401") || msg.contains("403") {
            Err(anyhow!("deepgram credentials rejected — check [transcription.deepgram].api_key"))
        } else if msg.contains("429") || msg.contains("50") {
            retry_with_backoff(|| provider.transcribe(&audio, name)).await
        } else {
            Err(e)
        }
    }
}

Prevention

When it happens

Trigger: POST https://api.deepgram.com/v1/listen returns non-2xx: 401 with invalid [transcription.deepgram] api_key; 400 when the audio bytes do not match a recognizable codec or the mimetype/extension is wrong; 402 when the Deepgram account is out of credits; 429 rate limited.

Common situations: Expired or mistyped Deepgram API key; free-tier credits exhausted; .oga/.ogg files whose actual codec is not Opus/FLAC/Ogg and Deepgram cannot sniff them; key rotated in the Deepgram console but config not updated.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/ddebfacc3886038b. Report an issue: GitHub.