decolua/9router · error · Error
OpenRouter TTS returned no audio data
Error message
OpenRouter TTS returned no audio data
What it means
After a successful HTTP response, the OpenRouter TTS handler parses the SSE stream and collects base64 audio chunks from choices[0].delta.audio.data. If zero chunks were collected, it throws this error. This can happen when the stream contains only text deltas, errors, or an immediate [DONE].
Source
Thrown at open-sse/handlers/ttsProviders/openrouter.js:70
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop();
for (const line of lines) {
if (!line.startsWith("data: ") || line === "data: [DONE]") continue;
try {
const json = JSON.parse(line.slice(6));
const audioData = json.choices?.[0]?.delta?.audio?.data;
if (audioData) chunks.push(audioData);
} catch {}
}
}
if (chunks.length === 0) throw new Error("OpenRouter TTS returned no audio data");
return { base64: chunks.join(""), format: "wav" };
},
};
View on GitHub (pinned to 90b52e06ff)
Solutions
- Verify the chosen model supports audio output modality on OpenRouter and include modality/audio params correctly
- Log the raw SSE lines to inspect the actual delta shape
- Confirm your OpenRouter account has access to audio-capable models
- Try a different TTS model/voice to isolate model-side issues
- Check OpenRouter status page for provider outages
Defensive patterns
Strategy: validation
Validate before calling
const res = await fetch("https://openrouter.ai/api/v1/models");
const models = (await res.json()).data.map(m => m.id);
if (!models.includes(ttsModel)) throw new Error(`Model ${ttsModel} not available on OpenRouter`); Type guard
function sseHasAudioChunk(line) {
try {
const j = JSON.parse(line.replace(/^data: /, ""));
return typeof j?.choices?.[0]?.delta?.audio?.data === "string" && j.choices[0].delta.audio.data.length > 0;
} catch { return false; }
} Try / catch
try {
return await openrouterTts.synthesize(text, model, creds);
} catch (e) {
if (e.message === "OpenRouter TTS returned no audio data") {
logger.warn("openrouter tts: empty audio stream, falling back", { model });
return fallbackTts(text);
}
throw e;
} Prevention
- Check the model's supported modalities on OpenRouter before enabling TTS routing
- Log raw SSE lines at debug level to detect payload-shape changes early
- Treat empty-audio as retriable once, then fall back to another provider
- Subscribe to OpenRouter changelog/status for audio API changes
When it happens
Trigger: OpenRouter returns 200 with an SSE stream that never includes delta.audio.data: the model produced no audio (modality not enabled for the model, 'audio' param rejected silently), stream closed early, or chunks arrive in a different JSON shape after an API change.
Common situations: Selecting an OpenRouter model that doesn't actually support audio output, account without audio modality access, upstream provider outage mid-stream, or OpenRouter changing the delta payload shape.
Related errors
- Upstream returned empty audio
- Bing TTS returned empty audio
- ElevenLabs TTS returned empty audio
- Gemini TTS returned no audio (finishReason: ${reason}, voice
- Google TTS returned empty audio
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/37d90ffd6b600dcb.
Report an issue: GitHub.