decolua/9router · error · Error
OpenAI TTS failed: ${res.status}
Error message
OpenAI TTS failed: ${res.status} What it means
Thrown when the OpenAI /v1/audio/speech endpoint returns a non-2xx HTTP status. The code first tries to surface OpenAI's structured error message (err.error.message) and falls back to this template containing the HTTP status code. It wraps any upstream rejection: invalid key, bad model, billing, rate limits.
Source
Thrown at open-sse/handlers/ttsProviders/openai.js:28
let ttsModel = DEFAULT_TTS_MODEL;
let voice = "alloy";
if (model && model.includes("/")) {
const parts = model.split("/");
if (parts.length === 2) [ttsModel, voice] = parts;
} else if (model) {
voice = model;
}
const baseUrl = (credentials.baseUrl || "https://api.openai.com").replace(/\/+$/, "");
const res = await fetch(`${baseUrl}/v1/audio/speech`, {
method: "POST",
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${credentials.apiKey}` },
body: JSON.stringify({ model: ttsModel, voice, input: text }),
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err?.error?.message || `OpenAI TTS failed: ${res.status}`);
}
const buf = await res.arrayBuffer();
return { base64: Buffer.from(buf).toString("base64"), format: "mp3" };
},
};
View on GitHub (pinned to 90b52e06ff)
Solutions
- Check the HTTP status in the message: 401 -> fix API key, 429 -> rate limit/billing, 404 -> model name
- Verify the OpenAI account has billing credit and the key is active
- Confirm the TTS model name (default from PROVIDER_MEDIA openai.ttsConfig.defaultModel) is valid, e.g. gpt-4o-mini-tts or tts-1
- Check network/proxy interference if status is 5xx or 403
- Retry with backoff on 429/5xx
Defensive patterns
Strategy: retry
Validate before calling
if (!credentials?.apiKey) throw new Error("OpenAI TTS: missing API key");
if (!/^(gpt-4o-mini-tts|tts-1|tts-1-hd)$/.test(ttsModel)) throw new Error(`Unknown OpenAI TTS model: ${ttsModel}`); Type guard
function isOpenAiHttpError(e) {
return e instanceof Error && /^OpenAI TTS failed: \d{3}$/.test(e.message);
} Try / catch
try {
return await openaiTts.synthesize(text, model, creds);
} catch (e) {
const m = e.message.match(/OpenAI TTS failed: (\d{3})/);
if (m && (m[1] === "429" || m[1].startsWith("5"))) return retryWithBackoff(() => openaiTts.synthesize(text, model, creds));
throw e;
} Prevention
- Handle 429 with exponential backoff and jitter
- Keep OpenAI billing credits topped up and alert on low balance
- Pin valid TTS model names in config and validate against PROVIDER_MEDIA
- Surface err.error.message (when present) to users instead of the status-only fallback
When it happens
Trigger: POST to OpenAI audio/speech fails: 401 invalid API key, 404 wrong model name (e.g. tts-1 typo), 429 rate limit, 402/429 billing/quota exceeded, or a proxy returning an error page without JSON parseable error.message.
Common situations: Expired or revoked OpenAI keys, insufficient credits, requesting a model the account lacks access to, or corporate proxies/VPNs returning non-JSON error bodies so the fallback status-code message appears.
Related errors
- Upstream error (${res.status})
- OpenRouter TTS failed: ${res.status}
- MiMo TTS error (${res.status})
- Upstream returned empty audio
- Edge TTS voices fetch failed: ${res.status}
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/c74072e6a0062f52.
Report an issue: GitHub.