decolua/9router · error · Error

ElevenLabs TTS returned empty audio

Error message

ElevenLabs TTS returned empty audio

What it means

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.

Source

Thrown at open-sse/handlers/ttsProviders/elevenlabs.js:45

    let modelId = "eleven_flash_v2_5";
    let voiceId = model;
    if (model && model.includes("/")) [modelId, voiceId] = model.split("/");

    const res = await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`, {
      method: "POST",
      headers: { "xi-api-key": credentials.apiKey, "Content-Type": "application/json" },
      body: JSON.stringify({
        text,
        model_id: modelId,
        voice_settings: { stability: 0.5, similarity_boost: 0.75 },
      }),
    });
    if (!res.ok) {
      const err = await res.json().catch(() => ({}));
      throw new Error(err?.detail?.message || `ElevenLabs TTS failed: ${res.status}`);
    }
    const buf = await res.arrayBuffer();
    if (buf.byteLength < 1024) throw new Error("ElevenLabs TTS returned empty audio");
    return { base64: Buffer.from(buf).toString("base64"), format: "mp3" };
  },
};

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Check that the input text is non-empty after trimming before calling synthesize.
  2. Retry the request once — brief upstream truncation is usually transient.
  3. 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.
  4. Check ElevenLabs status/your account quota; some degraded modes return empty audio with 200.
  5. Fall back to another TTS provider for the affected request.

Example fix

// before
await elevenlabs.synthesize(text, model, creds); // text may be ""
// after
const t = (text || "").trim();
if (!t) throw new Error("TTS text is empty");
await elevenlabs.synthesize(t, model, creds);
Defensive patterns

Strategy: validation

Validate before calling

if (!text?.trim()) throw new Error("TTS text is empty");
if (text.trim().length < 1) throw new Error("TTS text too short to synthesize");

Type guard

function isSynthesizableText(t) {
  return typeof t === "string" && t.trim().length > 0;
}

Try / catch

try {
  const audio = await elevenlabs.synthesize(text, model, creds);
} catch (e) {
  if (e.message === "ElevenLabs TTS returned empty audio") {
    // transient upstream issue — retry once, then fall back
    try { return await elevenlabs.synthesize(text, model, creds); }
    catch { return await fallbackTts(text); }
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/9adaf09362f296e9. Report an issue: GitHub.