zeroclaw-labs/zeroclaw · error
AssemblyAI transcription error ({}): {}
Error message
AssemblyAI transcription error ({}): {} What it means
Step two of the AssemblyAI pipeline: after a successful upload, the provider POSTs to /v2/transcript with the returned upload_url to create the transcription job, and that call returned non-2xx. The status and body 'error' field are embedded. The audio bytes were already uploaded, but no transcript job exists, so polling never starts.
Source
Thrown at crates/zeroclaw-channels/src/transcription.rs:538
let create_resp = client
.post("https://api.assemblyai.com/v2/transcript")
.header("Authorization", &self.api_key)
.json(&transcript_req)
.timeout(std::time::Duration::from_secs(TRANSCRIPTION_TIMEOUT_SECS))
.send()
.await
.context("Failed to create AssemblyAI transcription")?;
let create_status = create_resp.status();
let create_body: serde_json::Value = create_resp
.json()
.await
.context("Failed to parse AssemblyAI create response")?;
if !create_status.is_success() {
let error_msg = create_body["error"].as_str().unwrap_or("unknown error");
bail!(
"AssemblyAI transcription error ({}): {}",
create_status,
error_msg
);
}
let transcript_id = create_body["id"]
.as_str()
.context("AssemblyAI response missing 'id'")?;
// Step 3: Poll for completion.
let poll_url = format!("https://api.assemblyai.com/v2/transcript/{transcript_id}");
let poll_interval = std::time::Duration::from_secs(3);
let poll_deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(180);
while tokio::time::Instant::now() < poll_deadline {
tokio::time::sleep(poll_interval).await;
View on GitHub (pinned to 88bb9c8533)
Solutions
- 401/403 — verify the api_key in [transcription.assemblyai]
- 400 — read the embedded 'error' text; usually an invalid option value or unsupported audio — simplify options and retry
- 429/5xx — retry transcribe() after a short backoff; upload and create are both retried from scratch
Defensive patterns
Strategy: try-catch
Try / catch
match provider.transcribe(&audio, name).await {
Ok(text) => Ok(text),
Err(e) if e.to_string().contains("AssemblyAI transcription error (4") => {
// 401/403 = credentials; 400 = read embedded error text; 429/5xx = retry
log_and_classify(e)
}
other => other,
} Prevention
- Pin the AssemblyAI options you send to well-documented values — 400s at create time are usually option mistakes
- Verify the key at startup with a GET /v2/transcript list call
When it happens
Trigger: POST /v2/transcript (with audio_url from the upload step and language/model options) returns non-success: 401 invalid api_key, 400 unsupported audio_url or invalid transcription options, 429 rate limited.
Common situations: Invalid AssemblyAI key that only fails at job creation (e.g. key valid for upload but restricted); mismatched language configuration for the audio; API changes in AssemblyAI request options between server versions.
Related errors
- AssemblyAI upload error ({}): {}
- Deepgram API error ({}): {}
- AssemblyAI poll error ({}): {}
- AssemblyAI transcription failed: {}
- AssemblyAI transcription timed out after 180s
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/d7a65d0c59d69ee2.
Report an issue: GitHub.