decolua/9router · error · Error
Self-hosted TTS failed: ${res.status}
Error message
Self-hosted TTS failed: ${res.status} What it means
Thrown when a self-hosted OpenAI-compatible TTS server (e.g. local Kokoro/Faster-Whisper style /v1/audio/speech) returns a non-2xx status. The handler prefers a JSON error.message from the body, falling back to this status-code template. It reflects your own server's rejection, not a cloud provider.
Source
Thrown at open-sse/handlers/ttsProviders/selfhostedTts.js:64
}
}
const res = await fetch(`${base}/v1/audio/speech`, {
method: "POST",
headers: {
"Content-Type": "application/json",
...(credentials?.apiKey ? { Authorization: `Bearer ${credentials.apiKey}` } : {}),
},
body: JSON.stringify({
model: ttsModel,
voice,
input: text,
response_format: responseFormat,
}),
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err?.error?.message || `Self-hosted TTS failed: ${res.status}`);
}
const buf = await res.arrayBuffer();
return { base64: Buffer.from(buf).toString("base64"), format: responseFormat };
},
};
View on GitHub (pinned to 90b52e06ff)
Solutions
- Check the HTTP status: 404 -> wrong base URL/path, 503 -> server still loading or down, 401 -> auth required
- Confirm the TTS server is running and the model is loaded (hit its health/docs endpoint)
- Verify base URL, port, and API version path in the 9Router provider config
- Request a response_format the server supports (e.g. mp3/wav)
- Inspect the self-hosted server logs for the underlying error
Defensive patterns
Strategy: retry
Validate before calling
const health = await fetch(`${baseUrl}/v1/models`).catch(() => null);
if (!health || !health.ok) throw new Error(`Self-hosted TTS unreachable at ${baseUrl}`); Type guard
function isSelfHostedHttpError(e) {
return e instanceof Error && /^Self-hosted TTS failed: \d{3}$/.test(e.message);
} Try / catch
try {
return await selfHostedTts.synthesize(text, model, opts);
} catch (e) {
const m = e.message.match(/Self-hosted TTS failed: (\d{3})/);
if (m && m[1] === "503") return retryWithBackoff(() => selfHostedTts.synthesize(text, model, opts)); // server warming up
throw e;
} Prevention
- Add a startup health probe against the self-hosted /v1/models endpoint
- Use a documented response_format the server actually supports
- Put the TTS server behind a stable internal hostname, not localhost inside containers
- Watch server logs and readiness endpoints before routing traffic
When it happens
Trigger: POST to the configured self-hosted base URL fails: server not fully started, wrong port/path, model not loaded on the server, unsupported response_format requested, or an auth middleware rejecting the request.
Common situations: Docker container still loading the model (503), reverse proxy returning 404/502, requesting a response_format the server doesn't implement, or firewall/local network issues producing 5xx from a proxy.
Related errors
- Edge TTS voices fetch failed: ${res.status}
- Upstream error (${res.status})
- Bing translator fetch failed: ${res.status}
- Bing TTS failed: ${res.status}${body ? " - " + body : ""}
- ElevenLabs voices fetch failed: ${res.status}
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/dd49714b9a5bb225.
Report an issue: GitHub.