decolua/9router · error
Bing TTS failed: ${res.status}${body ? " - " + body : ""}
Error message
Bing TTS failed: ${res.status}${body ? " - " + body : ""} What it means
The Bing/Edge TTS provider (open-sse/handlers/ttsProviders/edgeTts.js) scrapes a free token from bing.com/translator and POSTs SSML to the undocumented `tfettts` endpoint. After one automatic retry on 429/403 (fresh token), it throws this error if the upstream HTTP status is still not OK, appending any response body it received. It is the library's catch-all for 'Bing refused or could not service this synthesis request'.
Source
Thrown at open-sse/handlers/ttsProviders/edgeTts.js:83
export default {
noAuth: true,
async synthesize(text, model) {
const voiceId = model || "vi-VN-HoaiMyNeural";
let token = await getToken();
let res = await ttsRequest(text, voiceId, token);
// 429/403: invalidate cache and retry once
if (res.status === 429 || res.status === 403) {
cache.token = null;
cache.tokenTime = 0;
token = await getToken();
res = await ttsRequest(text, voiceId, token);
}
if (!res.ok) {
const body = await res.text().catch(() => "");
throw new Error(`Bing TTS failed: ${res.status}${body ? " - " + body : ""}`);
}
const buf = await res.arrayBuffer();
if (buf.byteLength < 1024) throw new Error("Bing TTS returned empty audio");
return { base64: Buffer.from(buf).toString("base64"), format: "mp3" };
},
};
View on GitHub (pinned to 90b52e06ff)
Solutions
- Read the status and body in the error message: 401/403/429 usually mean the scraped token scheme changed or you are rate-limited — retry later or reduce request rate.
- Sanitize/escape the input text before passing it to the TTS call (strip or XML-escape characters like <, >, &) so the generated SSML stays well-formed.
- Retry the request once after a short delay — the module caches a Bing token for ~5 minutes; a stale token from a previous run can fail until the next refresh cycle.
- If 403/429 persists across retries, treat the Bing free endpoint as unavailable and fall back to a paid TTS provider (e.g. ElevenLabs or Gemini) configured in the dashboard.
- Check network egress: if you run behind a proxy or from a cloud IP, test https://www.bing.com/translator reachability; consider routing via a residential/different region.
Example fix
// before: raw user text injected into SSML
const ssml = `<speak ...>${text}</speak>`;
// after: XML-escape text before interpolation
const esc = String(text).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
const ssml = `<speak ...>${esc}</speak>`; Defensive patterns
Strategy: retry
Validate before calling
// before calling synthesize
if (!text || !text.trim()) throw new Error("TTS text is empty");
const ssmlUnsafe = /[<>&]/.test(text);
if (ssmlUnsafe) text = text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); Try / catch
try {
const audio = await edgeTts.synthesize(text, voiceId);
} catch (e) {
if (/Bing TTS failed: (429|403)/.test(e.message)) {
await sleep(2000);
return edgeTts.synthesize(text, voiceId); // one retry; fresh token is fetched inside
}
if (/Bing TTS failed: 5\d\d/.test(e.message)) return fallbackTts(text);
throw e;
} Prevention
- XML-escape all user text before TTS (it is interpolated into SSML).
- Keep per-IP request rates low — Bing rate-limits the free endpoint aggressively.
- Prefer paid TTS providers for production traffic; treat Bing/Edge as a free fallback.
- Monitor for persistent 403/429 — it usually means Bing changed its token scheme.
When it happens
Trigger: Calling the TTS synthesize path with the edge/bing provider when the POST to https://www.bing.com/tfettts returns any non-OK status after the single token-refresh retry — e.g. 400 from malformed text/SSML, 403/429 that persists even after token invalidation (rate limit or regional block), 5xx from Bing, or an empty-text request producing a server error.
Common situations: Bing changes its undocumented endpoint or abuse-prevention scheme and tokens stop working; heavy automated use trips Bing's rate limiter so even a fresh token gets 429; running from a datacenter IP or region Bing blocks; text containing characters that break the hand-built SSML (e.g. unescaped `<>` or quotes) yielding 400.
Related errors
- ElevenLabs voices fetch failed: ${res.status}
- ElevenLabs TTS failed: ${res.status}
- Gemini TTS failed: ${res.status}
- Bing translator fetch failed: ${res.status}
- Edge TTS voices fetch failed: ${res.status}
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/c3ed63997eacea07.
Report an issue: GitHub.