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

  1. Verify the board URL still resolves to a real XHS board page in a browser (not a login/error page).
  2. Check whether XHS still emits window.__INITIAL_SSR_STATE__ — if renamed, update the selector and regex in extractInitialSsrState.
  3. 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.
  4. Confirm the proxy returns the raw XHS HTML and not an upstream proxy error page.
  5. 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

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


AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12). Data as JSON: /api/errors/bff708e1e1571f6e. Report an issue: GitHub.