DIYgod/RSSHub · error · Error
Cannot extract __INITIAL_SSR_STATE__
Error message
Cannot extract __INITIAL_SSR_STATE__
What it means
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.
Source
Thrown at lib/routes/xiaohongshu/util.ts:326
return state.user;
}
// Add helper function to extract initial state
function extractInitialState($: CheerioAPI) {
let script = $('script:contains("window.__INITIAL_STATE__=")').text();
script = script.slice(script.indexOf('window.__INITIAL_STATE__=') + 'window.__INITIAL_STATE__='.length);
script = script.replaceAll('undefined', 'null');
return script;
}
// Add helper function to extract initial SSR state
function extractInitialSsrState($: CheerioAPI) {
const script = $('script:contains("window.__INITIAL_SSR_STATE__=")').text();
const match = script.match(/window\.__INITIAL_SSR_STATE__\s*=\s*(\{[\s\S]*?\})\s*(?:;|$)/);
if (match) {
return match[1].replaceAll('undefined', 'null');
}
throw new Error('Cannot extract __INITIAL_SSR_STATE__');
}
async function checkCookie() {
const cookie = config.xiaohongshu.cookie;
const res = await ofetch('https://edith.xiaohongshu.com/api/sns/web/v2/user/me', {
headers: getHeaders(cookie),
});
return res.code === 0 && !!res.data.user_id;
}
export { checkCookie, formatNote, formatText, getBoard, getFullNote, getUser, getUserWithCookie, renderNotesFulltext };
View on GitHub (pinned to bed535e087)
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 (?:;|$)).
Example fix
// before
const match = script.match(/window\.__INITIAL_SSR_STATE__\s*=\s*(\{[\s\S]*?\})\s*(?:;|$)/);
if (match) {
return match[1].replaceAll('undefined', 'null');
}
throw new Error('Cannot extract __INITIAL_SSR_STATE__');
// after — capture up to the matching closing brace via a greedy-but-balanced slice, and diagnose the missing case
const start = script.indexOf('{');
if (start === -1) {
throw new Error('Cannot extract __INITIAL_SSR_STATE__: script tag present but no JSON object found');
}
return script.slice(start).replaceAll('undefined', 'null'); Defensive patterns
Strategy: try-catch
Validate before calling
// Inspect the fetched HTML before parsing SSR state
function hasInitialSsrState(html: string): boolean {
return html.includes('window.__INITIAL_SSR_STATE__=');
} Try / catch
let state;
try {
state = JSON.parse(extractInitialSsrState($));
} catch (e) {
// Fallback: try Playwright rendering, or throw a clearer error including the page title
const title = $('title').text();
throw new Error(`extractInitialSsrState failed (page title: "${title}"). The board URL may be invalid or XHS changed its SSR shape: ${e.message}`);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- 无法获取最新的 year 和 issue
- Cannot find n-token
- WapHomeRenderData 数据未找到
- Cannot get link
- 小红书未返回用户数据,请稍后再试: ${JSON.stringify(userPageData.result)}
AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12).
Data as JSON: /api/errors/bff708e1e1571f6e.
Report an issue: GitHub.