{"record":{"id":"c3ed63997eacea07","repo":"decolua/9router","slug":"bing-tts-failed-res-status-body-bod","errorCode":null,"errorMessage":"Bing TTS failed: ${res.status}${body ? \" - \" + body : \"\"}","messagePattern":"Bing TTS failed: (.+?)(.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/handlers/ttsProviders/edgeTts.js","lineNumber":83,"sourceCode":"\nexport default {\n  noAuth: true,\n  async synthesize(text, model) {\n    const voiceId = model || \"vi-VN-HoaiMyNeural\";\n    let token = await getToken();\n    let res = await ttsRequest(text, voiceId, token);\n\n    // 429/403: invalidate cache and retry once\n    if (res.status === 429 || res.status === 403) {\n      cache.token = null;\n      cache.tokenTime = 0;\n      token = await getToken();\n      res = await ttsRequest(text, voiceId, token);\n    }\n\n    if (!res.ok) {\n      const body = await res.text().catch(() => \"\");\n      throw new Error(`Bing TTS failed: ${res.status}${body ? \" - \" + body : \"\"}`);\n    }\n    const buf = await res.arrayBuffer();\n    if (buf.byteLength < 1024) throw new Error(\"Bing TTS returned empty audio\");\n    return { base64: Buffer.from(buf).toString(\"base64\"), format: \"mp3\" };\n  },\n};\n","sourceCodeStart":65,"sourceCodeEnd":90,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/handlers/ttsProviders/edgeTts.js#L65-L90","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: raw user text injected into SSML\nconst ssml = `<speak ...>${text}</speak>`;\n// after: XML-escape text before interpolation\nconst esc = String(text).replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\nconst ssml = `<speak ...>${esc}</speak>`;","handlingStrategy":"retry","validationCode":"// before calling synthesize\nif (!text || !text.trim()) throw new Error(\"TTS text is empty\");\nconst ssmlUnsafe = /[<>&]/.test(text);\nif (ssmlUnsafe) text = text.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");","typeGuard":null,"tryCatchPattern":"try {\n  const audio = await edgeTts.synthesize(text, voiceId);\n} catch (e) {\n  if (/Bing TTS failed: (429|403)/.test(e.message)) {\n    await sleep(2000);\n    return edgeTts.synthesize(text, voiceId); // one retry; fresh token is fetched inside\n  }\n  if (/Bing TTS failed: 5\\d\\d/.test(e.message)) return fallbackTts(text);\n  throw e;\n}","preventionTips":["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."],"tags":["network","tts","upstream-provider","http-status"],"backgroundTag":"upstream-http-error","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}