decolua/9router · error · Error
Inworld TTS returned no audio
Error message
Inworld TTS returned no audio
What it means
The Inworld TTS handler POSTs `{ input, modelId, audioConfig:{audioEncoding:'MP3'} }` to Inworld's endpoint. After a 2xx response it requires the `audioContent` field (base64 MP3) in the JSON body; if absent it throws this error. It means Inworld acknowledged the request but did not return audio in the expected field.
Source
Thrown at open-sse/handlers/ttsProviders/genericFormats.js:87
if (!res.ok) await throwUpstreamError(res);
return responseToBase64(res, "mp3");
}
// Inworld: Basic auth, JSON { audioContent }
async function inworld({ baseUrl, apiKey, text, modelId, voiceId }) {
const res = await fetch(baseUrl, {
method: "POST",
headers: { "Content-Type": "application/json", "Authorization": `Basic ${apiKey}` },
body: JSON.stringify({
text,
voiceId: voiceId || "Alex",
modelId: modelId || "inworld-tts-1.5-mini",
audioConfig: { audioEncoding: "MP3" },
}),
});
if (!res.ok) await throwUpstreamError(res);
const data = await res.json();
if (!data.audioContent) throw new Error("Inworld TTS returned no audio");
return { base64: data.audioContent, format: "mp3" };
}
// Cartesia: X-API-Key header
async function cartesia({ baseUrl, apiKey, text, modelId, voiceId }) {
const res = await fetch(baseUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": apiKey,
"Cartesia-Version": "2024-06-10",
},
body: JSON.stringify({
model_id: modelId || "sonic-2",
transcript: text,
...(voiceId ? { voice: { mode: "id", id: voiceId } } : {}),
output_format: { container: "mp3", bit_rate: 128000, sample_rate: 44100 },
}),View on GitHub (pinned to 90b52e06ff)
Solutions
- Log the raw response body at this point to see what Inworld actually returned
- Verify the Inworld TTS endpoint URL and payload schema against current docs (field may now be nested, e.g. `result.audioContent`)
- Check the voice ID / API key / quota — some Inworld errors come back as 200 with a message field instead of audio
- Pin/upgrade to the current Inworld API version and update the handler if the field moved
Example fix
// before
const data = await res.json();
if (!data.audioContent) throw new Error('Inworld TTS returned no audio');
// after (tolerate nested shape)
const data = await res.json();
const b64 = data.audioContent ?? data.result?.audioContent ?? data.audio?.audioContent;
if (!b64) throw new Error('Inworld TTS returned no audio: ' + JSON.stringify(data).slice(0, 200)); Defensive patterns
Strategy: try-catch
Validate before calling
if (!text || typeof text !== 'string') throw new Error('text required for Inworld TTS'); Type guard
const hasAudio = (d) => d != null && typeof d === 'object' && typeof d.audioContent === 'string' && d.audioContent.length > 0;
Try / catch
try { return await inworldTts(...); } catch (e) { if (e.message.includes('no audio')) { logger.warn('inworld empty payload'); return fallbackTts(...); } throw e; } Prevention
- Log the raw Inworld response when this fires to detect schema drift early
- Pin the Inworld API version and review changelogs
- Keep a fallback TTS provider configured
When it happens
Trigger: Upstream returns 200 with a JSON body lacking `audioContent` — e.g. response shape changed (API version drift), or an error/muted/empty payload embedded in a 200 response.
Common situations: Inworld API contract change (field renamed or nested differently); account/voice misconfiguration returning a silent empty response; proxy/gateway returning a 200 HTML or JSON error envelope instead of the TTS payload.
Related errors
- Upstream returned empty audio
- Upstream error (${res.status})
- Google TTS failed: ${res.status}
- MiniMax TTS error (${res.status})
- OpenAI TTS failed: ${res.status}
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/84d615c697817773.
Report an issue: GitHub.