{"record":{"id":"9adaf09362f296e9","repo":"decolua/9router","slug":"elevenlabs-tts-returned-empty-audio","errorCode":null,"errorMessage":"ElevenLabs TTS returned empty audio","messagePattern":"ElevenLabs TTS returned empty audio","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"open-sse/handlers/ttsProviders/elevenlabs.js","lineNumber":45,"sourceCode":"    let modelId = \"eleven_flash_v2_5\";\n    let voiceId = model;\n    if (model && model.includes(\"/\")) [modelId, voiceId] = model.split(\"/\");\n\n    const res = await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`, {\n      method: \"POST\",\n      headers: { \"xi-api-key\": credentials.apiKey, \"Content-Type\": \"application/json\" },\n      body: JSON.stringify({\n        text,\n        model_id: modelId,\n        voice_settings: { stability: 0.5, similarity_boost: 0.75 },\n      }),\n    });\n    if (!res.ok) {\n      const err = await res.json().catch(() => ({}));\n      throw new Error(err?.detail?.message || `ElevenLabs TTS failed: ${res.status}`);\n    }\n    const buf = await res.arrayBuffer();\n    if (buf.byteLength < 1024) throw new Error(\"ElevenLabs TTS returned empty audio\");\n    return { base64: Buffer.from(buf).toString(\"base64\"), format: \"mp3\" };\n  },\n};\n","sourceCodeStart":27,"sourceCodeEnd":49,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/handlers/ttsProviders/elevenlabs.js#L27-L49","documentation":"After a 200 response, ElevenLabs synthesize requires the audio body to be at least 1024 bytes before returning base64 MP3; anything smaller throws \"ElevenLabs TTS returned empty audio\". This catches responses that succeeded at the HTTP layer but contain no usable audio payload.","triggerScenarios":"ElevenLabs returns 200 with a tiny body — usually an empty or near-empty response when the voice/model produced no audio, a truncated response on connection issues, or when the request contained empty/whitespace text so nothing was synthesized.","commonSituations":"Passing empty or whitespace-only text through the pipeline; extremely short text with certain voices producing sub-1KB payloads at low bitrates; ElevenLabs-side incidents returning empty 200s; chunked-response truncation through a proxy.","solutions":["Check that the input text is non-empty after trimming before calling synthesize.","Retry the request once — brief upstream truncation is usually transient.","If the text is genuinely tiny (a single word), the <1KB threshold may false-positive; test the same text with a longer string to confirm, and consider treating the payload as valid.","Check ElevenLabs status/your account quota; some degraded modes return empty audio with 200.","Fall back to another TTS provider for the affected request."],"exampleFix":"// before\nawait elevenlabs.synthesize(text, model, creds); // text may be \"\"\n// after\nconst t = (text || \"\").trim();\nif (!t) throw new Error(\"TTS text is empty\");\nawait elevenlabs.synthesize(t, model, creds);","handlingStrategy":"validation","validationCode":"if (!text?.trim()) throw new Error(\"TTS text is empty\");\nif (text.trim().length < 1) throw new Error(\"TTS text too short to synthesize\");","typeGuard":"function isSynthesizableText(t) {\n  return typeof t === \"string\" && t.trim().length > 0;\n}","tryCatchPattern":"try {\n  const audio = await elevenlabs.synthesize(text, model, creds);\n} catch (e) {\n  if (e.message === \"ElevenLabs TTS returned empty audio\") {\n    // transient upstream issue — retry once, then fall back\n    try { return await elevenlabs.synthesize(text, model, creds); }\n    catch { return await fallbackTts(text); }\n  }\n  throw e;\n}","preventionTips":["Reject empty/whitespace-only text before calling synthesize.","Treat single occurrences as transient and retry once.","Check ElevenLabs status/incidents if empty 200s cluster in time.","Fall back to another provider so a single empty response does not fail the user request."],"tags":["tts","empty-response","validation"],"backgroundTag":"empty-response-body","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}