{"record":{"id":"bff708e1e1571f6e","repo":"DIYgod/RSSHub","slug":"cannot-extract-initial-ssr-state","errorCode":null,"errorMessage":"Cannot extract __INITIAL_SSR_STATE__","messagePattern":"Cannot extract __INITIAL_SSR_STATE__","errorType":"exception","errorClass":"Error","httpStatus":503,"severity":"error","filePath":"lib/routes/xiaohongshu/util.ts","lineNumber":326,"sourceCode":"    return state.user;\n}\n\n// Add helper function to extract initial state\nfunction extractInitialState($: CheerioAPI) {\n    let script = $('script:contains(\"window.__INITIAL_STATE__=\")').text();\n    script = script.slice(script.indexOf('window.__INITIAL_STATE__=') + 'window.__INITIAL_STATE__='.length);\n    script = script.replaceAll('undefined', 'null');\n    return script;\n}\n\n// Add helper function to extract initial SSR state\nfunction extractInitialSsrState($: CheerioAPI) {\n    const script = $('script:contains(\"window.__INITIAL_SSR_STATE__=\")').text();\n    const match = script.match(/window\\.__INITIAL_SSR_STATE__\\s*=\\s*(\\{[\\s\\S]*?\\})\\s*(?:;|$)/);\n    if (match) {\n        return match[1].replaceAll('undefined', 'null');\n    }\n    throw new Error('Cannot extract __INITIAL_SSR_STATE__');\n}\n\nasync function checkCookie() {\n    const cookie = config.xiaohongshu.cookie;\n    const res = await ofetch('https://edith.xiaohongshu.com/api/sns/web/v2/user/me', {\n        headers: getHeaders(cookie),\n    });\n    return res.code === 0 && !!res.data.user_id;\n}\n\nexport { checkCookie, formatNote, formatText, getBoard, getFullNote, getUser, getUserWithCookie, renderNotesFulltext };\n","sourceCodeStart":308,"sourceCodeEnd":338,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/xiaohongshu/util.ts#L308-L338","documentation":"Thrown by the extractInitialSsrState helper in lib/routes/xiaohongshu/util.ts when it cannot match the window.__INITIAL_SSR_STATE__ assignment in the board page HTML via cheerio. It is used by getBoard (proxy path) to obtain state.Main. A plain Error, it signals a structural scraping failure: the page either did not inline the SSR state blob or the regex /window\\.__INITIAL_SSR_STATE__\\s*=\\s*(\\{[\\s\\S]*?\\})\\s*(?:;|$)/ could not capture it.","triggerScenarios":"getBoard's fetchWithProxy branch fetches the board URL, loads it with cheerio, then calls extractInitialSsrState. The selector $('script:contains(\"window.__INITIAL_SSR_STATE__=\")') returns empty (XHS served a non-board page: error, login wall, or 404), or the script exists but the closing brace was not on a line boundary / nested braces broke the non-greedy capture. Also triggered when the proxy returns HTML that is not the XHS board SPA.","commonSituations":"XHS renamed or removed __INITIAL_SSR_STATE__ in a frontend release; board URL changed or the board was deleted; proxy misconfiguration returns a gateway error page; the fetch path (no Playwright) is used against a page that only injects SSR state after JS execution, which a plain fetch cannot run.","solutions":["Verify the board URL still resolves to a real XHS board page in a browser (not a login/error page).","Check whether XHS still emits window.__INITIAL_SSR_STATE__ — if renamed, update the selector and regex in extractInitialSsrState.","If the page requires JS to populate SSR state, switch getBoard to the Playwright branch (remove/avoid config.xiaohongshu.proxy for boards) so the SPA can render.","Confirm the proxy returns the raw XHS HTML and not an upstream proxy error page.","Loosen the regex terminator if XHS now appends content after the JSON (e.g. match to a balanced brace parser instead of (?:;|$))."],"exampleFix":"// before\nconst match = script.match(/window\\.__INITIAL_SSR_STATE__\\s*=\\s*(\\{[\\s\\S]*?\\})\\s*(?:;|$)/);\nif (match) {\n    return match[1].replaceAll('undefined', 'null');\n}\nthrow new Error('Cannot extract __INITIAL_SSR_STATE__');\n\n// after — capture up to the matching closing brace via a greedy-but-balanced slice, and diagnose the missing case\nconst start = script.indexOf('{');\nif (start === -1) {\n    throw new Error('Cannot extract __INITIAL_SSR_STATE__: script tag present but no JSON object found');\n}\nreturn script.slice(start).replaceAll('undefined', 'null');","handlingStrategy":"try-catch","validationCode":"// Inspect the fetched HTML before parsing SSR state\nfunction hasInitialSsrState(html: string): boolean {\n    return html.includes('window.__INITIAL_SSR_STATE__=');\n}","typeGuard":null,"tryCatchPattern":"let state;\ntry {\n    state = JSON.parse(extractInitialSsrState($));\n} catch (e) {\n    // Fallback: try Playwright rendering, or throw a clearer error including the page title\n    const title = $('title').text();\n    throw new Error(`extractInitialSsrState failed (page title: \"${title}\"). The board URL may be invalid or XHS changed its SSR shape: ${e.message}`);\n}","preventionTips":["Validate the board URL returns an XHS SPA page before parsing (check for the script tag).","Prefer the Playwright branch of getBoard for pages that inject SSR state only after JS execution.","Pin a working snapshot of the regex; add a test fixture of a real board HTML to catch XHS frontend changes.","Log the page title when extraction fails so the cause (login wall / 404 / redesign) is obvious."],"tags":["xiaohongshu","scraping","regex","ssr","cheerio"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}