zeroclaw-labs/zeroclaw · error
ElevenLabs TTS API error ({}): {}
Error message
ElevenLabs TTS API error ({}): {} What it means
Raised by ElevenLabsTtsProvider::synthesize when POST /v1/text-to-speech/{voice_id} returns non-2xx. Authentication uses the xi-api-key header from [providers.tts.elevenlabs.<alias>].api_key. The message extracts detail.message (or a string detail) from the ElevenLabs error body; FastAPI-style validation errors surface there as "unknown error" when the shape differs.
Source
Thrown at crates/zeroclaw-channels/src/tts.rs:260
.client
.post(&url)
.header("xi-api-key", &self.api_key)
.json(&body)
.send()
.await
.context("Failed to send ElevenLabs TTS request")?;
let status = resp.status();
if !status.is_success() {
let error_body: serde_json::Value = resp
.json()
.await
.unwrap_or_else(|_| serde_json::json!({"error": "unknown"}));
let msg = error_body["detail"]["message"]
.as_str()
.or_else(|| error_body["detail"].as_str())
.unwrap_or("unknown error");
bail!("ElevenLabs TTS API error ({}): {}", status, msg);
}
let bytes = resp
.bytes()
.await
.context("Failed to read ElevenLabs TTS response body")?;
Ok(bytes.to_vec())
}
fn supported_voices(&self) -> Vec<String> {
// ElevenLabs voices are user-specific; return empty (dynamic lookup).
Vec::new()
}
fn supported_formats(&self) -> Vec<String> {
["mp3", "pcm", "ulaw"]
.iter()
.map(|s| (*s).to_string())View on GitHub (pinned to 88bb9c8533)
Solutions
- For 401/quota messages, verify the key at [providers.tts.elevenlabs.<alias>].api_key (env grammar ZEROCLAW_providers__tts__elevenlabs__<alias>__api_key) and check the subscription usage page.
- For voice-not-found, confirm the voice ID exists on that account via GET /v1/voices and fix [providers.tts.elevenlabs.<alias>].voice.
- For 422, set model_id to a current model (eleven_multilingual_v2, eleven_turbo_v2_5) and keep stability/similarity_boost in 0.0..=1.0.
- Retry once with backoff on transient 5xx or rate-limit responses.
Example fix
# before — env var no longer read since V0.8.0 # export ELEVENLABS_API_KEY=... # after — key lives in config (or ZEROCLAW_providers__tts__elevenlabs__main__api_key) [providers.tts.elevenlabs.main] api_key = "xi-..." model = "eleven_multilingual_v2"
Defensive patterns
Strategy: retry
Validate before calling
// Optional pre-flight: confirm the voice ID belongs to this account.
async fn elevenlabs_voice_exists(client: &reqwest::Client, key: &str, voice_id: &str) -> bool {
client
.get(format!("https://api.elevenlabs.io/v1/voices/{voice_id}"))
.header("xi-api-key", key)
.send()
.await
.map(|r| r.status().is_success())
.unwrap_or(false)
} Try / catch
let out = mgr.synthesize(text).await;
if let Err(ref err) = out {
let msg = err.to_string();
if msg.contains("ElevenLabs TTS API error (401)") {
// quota exhausted or bad key: stop retrying, surface for credential fix
} else if msg.contains("(422)") {
// model_id / voice_settings invalid: fix config, do not retry
} else if msg.contains("(42") || msg.contains("(5") {
// rate limit / transient: retry with backoff
tokio::time::sleep(Duration::from_secs(3)).await;
return mgr.synthesize(text).await;
}
}
out Prevention
- Since V0.8.0 the key must live in [providers.tts.elevenlabs.<alias>].api_key; drop ELEVENLABS_API_KEY from deployment scripts.
- Track free-tier character quota in your own metrics; ElevenLabs returns 401 on exhaustion, which looks like a bad key.
- Pin model to a currently documented model_id; check the ElevenLabs changelog after upgrades.
When it happens
Trigger: 401 with an invalid or free-tier-exhausted key, 422 when voice_settings (stability/similarity_boost) or model_id (default eleven_monolingual_v1) is invalid, 404/401-style rejection when the voice ID does not belong to the account, and rate-limit rejections on the starter plan.
Common situations: The ELEVENLABS_API_KEY env fallback was eradicated in V0.8.0, so upgraded deployments silently lose the key and send an empty or placeholder value. Free-tier keys hit the character quota mid-run. A model_id deprecated by ElevenLabs (old multilingual v1 names) starts failing after upstream changes.
Related errors
- Google TTS API error ({}): {}
- OpenAI TTS API error ({}): {}
- Failed to initiate call: {}
- ElevenLabs voice ID contains invalid characters: {voice}
- Piper TTS API error ({}): {}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/08a6a7c4d212b085.
Report an issue: GitHub.