{"record":{"id":"60e130e4a049ae05","repo":"decolua/9router","slug":"failed-to-parse-google-token","errorCode":null,"errorMessage":"Failed to parse Google token","messagePattern":"Failed to parse Google token","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"open-sse/handlers/ttsProviders/googleTts.js","lineNumber":16,"sourceCode":"// Google Translate TTS (no auth) — scrape token + batchexecute RPC\nimport { UA } from \"./_base.js\";\n\nconst REFRESH_MS = 11 * 60 * 1000;\nconst cache = { token: null, tokenTime: 0 };\nlet _idx = 0;\n\nasync function getToken() {\n  const now = Date.now();\n  if (cache.token && now - cache.tokenTime < REFRESH_MS) return cache.token;\n  const res = await fetch(\"https://translate.google.com/\", { headers: { \"User-Agent\": UA } });\n  if (!res.ok) throw new Error(`Google translate fetch failed: ${res.status}`);\n  const html = await res.text();\n  const fSid = html.match(/\"FdrFJe\":\"(.*?)\"/)?.[1];\n  const bl = html.match(/\"cfb2h\":\"(.*?)\"/)?.[1];\n  if (!fSid || !bl) throw new Error(\"Failed to parse Google token\");\n  cache.token = { \"f.sid\": fSid, bl };\n  cache.tokenTime = now;\n  return cache.token;\n}\n\nexport default {\n  noAuth: true,\n  async synthesize(text, model) {\n    const lang = model || \"en\";\n    const token = await getToken();\n    const cleanText = text.replace(/[@^*()\\\\/\\-_+=><\"'\\u201c\\u201d\\u3010\\u3011]/g, \" \").replaceAll(\", \", \". \");\n    const rpcId = \"jQ1olc\";\n    const reqId = (++_idx * 100000) + Math.floor(1000 + Math.random() * 9000);\n    const query = new URLSearchParams({\n      rpcids: rpcId,\n      \"f.sid\": token[\"f.sid\"],\n      bl: token.bl,\n      hl: lang,","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/handlers/ttsProviders/googleTts.js#L1-L34","documentation":"After fetching the Google Translate page, the code scrapes two values from inline JS via regex: `\"FdrFJe\":\"...\"` (f.sid) and `\"cfb2h\":\"...\"` (bl). If either regex fails to match, it throws this error. It indicates the page HTML no longer contains the expected token fields — the unofficial scraping contract broke.","triggerScenarios":"A 200 response whose body is a consent page, bot-check/CAPTCHA page, or a redesigned translate.google.com UI without those keys; gzip/compressed body not decoded; localized or A/B variants of the page.","commonSituations":"Google frontend update renaming the obfuscated keys (FdrFJe/cfb2h); bot mitigation serving challenge HTML to datacenter IPs; region-specific consent interstitials; a proxy stripping or transforming the HTML.","solutions":["Dump the response HTML and inspect which of `FdrFJe` / `cfb2h` is missing and what the page actually contains","Update the regexes/keys to the new page format, or pin to a maintained library (e.g. google-tts-api) that tracks these changes","Bypass scraping by supplying static token values (bl is commonly a version like `boq_translate-webserver_...`; many clients hardcode a working bl)","Route via a residential IP / add cookies to avoid challenge pages"],"exampleFix":"// before\nconst fSid = html.match(/\"FdrFJe\":\"(.*?)\"/)?.[1];\nconst bl = html.match(/\"cfb2h\":\"(.*?)\"/)?.[1];\nif (!fSid || !bl) throw new Error('Failed to parse Google token');\n// after (fallback to known-good static bl)\nconst bl = html.match(/\"cfb2h\":\"(.*?)\"/)?.[1] || 'boq_translate-webserver_20240101.00_p0';\nconst fSid = html.match(/\"FdrFJe\":\"(.*?)\"/)?.[1] || '-1';","handlingStrategy":"fallback","validationCode":"null","typeGuard":"const token = { fSid: html?.match(/\"FdrFJe\":\"(.*?)\"/)?.[1], bl: html?.match(/\"cfb2h\":\"(.*?)\"/)?.[1] };","tryCatchPattern":"try { return await googleTts(text); } catch (e) { if (e.message === 'Failed to parse Google token') return otherProviderTts(text); throw e; }","preventionTips":["Assume the scraping contract can break any time; never let Google-translate TTS be the only TTS path","Test the regex extraction in CI against a live page fetch to detect Google UI changes","Cache the last known-good token and reuse it when scraping fails"],"tags":["parsing","scraping","google","tts","regression"],"backgroundTag":"html-parse-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}