{"record":{"id":"ddebfacc3886038b","repo":"zeroclaw-labs/zeroclaw","slug":"deepgram-api-error","errorCode":null,"errorMessage":"Deepgram API error ({}): {}","messagePattern":"Deepgram API error \\((.+?)\\): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/transcription.rs","lineNumber":415,"sourceCode":"            .header(\"Content-Type\", mime)\n            .body(audio_data.to_vec())\n            .timeout(std::time::Duration::from_secs(TRANSCRIPTION_TIMEOUT_SECS))\n            .send()\n            .await\n            .context(\"Failed to send transcription request to Deepgram\")?;\n\n        let status = resp.status();\n        let body: serde_json::Value = resp\n            .json()\n            .await\n            .context(\"Failed to parse Deepgram response\")?;\n\n        if !status.is_success() {\n            let error_msg = body[\"err_msg\"]\n                .as_str()\n                .or_else(|| body[\"error\"].as_str())\n                .unwrap_or(\"unknown error\");\n            bail!(\"Deepgram API error ({}): {}\", status, error_msg);\n        }\n\n        let text = body[\"results\"][\"channels\"][0][\"alternatives\"][0][\"transcript\"]\n            .as_str()\n            .context(\"Deepgram response missing transcript field\")?\n            .to_string();\n\n        Ok(text)\n    }\n}\n\n// ── AssemblyAiProvider ──────────────────────────────────────────\n\n/// AssemblyAI STT API transcription_provider.\npub struct AssemblyAiProvider {\n    alias: String,\n    api_key: String,\n}","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/transcription.rs#L397-L433","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read {status}: 401/403 means the api_key in [transcription.deepgram] is wrong or revoked — update it","400 means Deepgram rejected the audio itself — verify the file extension matches the real encoding (re-encode with ffmpeg if needed)","402 means out of quota — check billing in the Deepgram console","429/5xx are transient — retry the transcription with backoff"],"exampleFix":"# before\n[transcription.deepgram]\napi_key = \"expired-key-abc\"\n\n# after\n[transcription.deepgram]\napi_key = \"valid-key-from-console\"","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match provider.transcribe(&audio, name).await {\n    Ok(text) => Ok(text),\n    Err(e) => {\n        let msg = e.to_string();\n        if msg.contains(\"401\") || msg.contains(\"403\") {\n            Err(anyhow!(\"deepgram credentials rejected — check [transcription.deepgram].api_key\"))\n        } else if msg.contains(\"429\") || msg.contains(\"50\") {\n            retry_with_backoff(|| provider.transcribe(&audio, name)).await\n        } else {\n            Err(e)\n        }\n    }\n}","preventionTips":["Validate the Deepgram key once at startup with a minimal /v1/listen request","Monitor Deepgram credit balance — 402 arrives without warning","Re-encode unusual codecs to mp3/flac before uploading"],"tags":["deepgram","http-status","api-key","transcription","quota"],"backgroundTag":"speech-to-text-api-error","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}