{"record":{"id":"5041d953f802e46c","repo":"jackwener/OpenCLI","slug":"webhost-feed-data-error","errorCode":null,"errorMessage":"${webHost} feed: ${data.error}","messagePattern":"\\$\\{webHost\\} feed: \\$\\{data\\.error\\}","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/feed.js","lineNumber":108,"sourceCode":"    return url.toString();\n}\n\n/**\n * Shared func-mode implementation. Exported so the rednote adapter can run the\n * same store read against www.rednote.com without duplicating the logic.\n */\nexport async function runFeed(page, kwargs, webHost) {\n    const limit = parseLimit(kwargs.limit);\n    await page.goto(`https://${webHost}/explore`);\n    // Pinia store hydrates from SSR; give the page a beat to finish\n    // bootstrapping before reading the array.\n    await page.wait({ time: 2 });\n    const data = unwrapEvaluateResult(await page.evaluate(FEEDS_READ_JS));\n    if (!data || typeof data !== 'object') {\n        throw new CommandExecutionError(`${webHost} feed: unexpected evaluate response`);\n    }\n    if (data.error) {\n        throw new CommandExecutionError(`${webHost} feed: ${data.error}`, `The SPA may still be hydrating; reload ${webHost}/explore and retry.`);\n    }\n    if (!Array.isArray(data.items)) {\n        throw new CommandExecutionError(`${webHost} feed: unexpected items payload shape`);\n    }\n    const rows = [];\n    for (const row of data.items) {\n        if (rows.length >= limit)\n            break;\n        if (!row || typeof row !== 'object') {\n            throw new CommandExecutionError(`${webHost} feed: malformed feed item`);\n        }\n        const id = toCleanString(row.id);\n        if (!id) {\n            throw new CommandExecutionError(`${webHost} feed: feed item is missing note id`);\n        }\n        const xsecToken = toCleanString(row.xsecToken);\n        if (!xsecToken) {\n            throw new CommandExecutionError(`${webHost} feed: feed item ${id} is missing xsecToken; cannot build a signed drill-down URL`);","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/feed.js#L90-L126","documentation":"When the in-page feed script returns an object carrying an error field, runFeed surfaces it as CommandExecutionError '<webHost> feed: <error>' with the hint that the SPA may still be hydrating. This is the site-side error propagated to the CLI.","triggerScenarios":"FEEDS_READ_JS executes successfully but the page-side script detects the feed data is not ready or an internal error occurred and returns { error: '...' }.","commonSituations":"Evaluating too early before Pinia hydration completes, slow network/SSR delays on /explore, rate limiting or anti-bot interstitials from the site, expired session causing the store to report an auth error.","solutions":["Retry after reloading https://<webHost>/explore and waiting for full render","Log in again if the session expired","Increase wait time or wait for a feed DOM selector before evaluating","Retry later if the site is rate limiting or showing anti-bot challenges","Update FEEDS_READ_JS if the store API changed"],"exampleFix":"// before\nawait page.wait({ time: 2 });\nconst data = unwrapEvaluateResult(await page.evaluate(FEEDS_READ_JS));\n// after\nawait page.goto(`https://${webHost}/explore`, { waitUntil: 'networkidle' });\nawait page.waitForSelector('.note-item');\nconst data = unwrapEvaluateResult(await page.evaluate(FEEDS_READ_JS));","handlingStrategy":"retry","validationCode":"await page.goto(`https://${webHost}/explore`, { waitUntil: 'networkidle' });\nawait page.waitForSelector('.note-item');","typeGuard":"function isHydratedFeed(d) { return !!d && typeof d === 'object' && !d.error; }","tryCatchPattern":"try {\n  await runFeed(page, webHost, limit);\n} catch (err) {\n  if (err instanceof CommandExecutionError && /feed: /.test(err.message) && /hydrating/.test(err.hint ?? '')) {\n    await page.reload(); await page.wait({ time: 5 }); // retry once after hydration\n  } else throw err;\n}","preventionTips":["Wait for network idle / feed selectors before evaluating","Keep the session alive; re-login on auth-related page errors","Back off and retry when the site rate-limits","Track site changes that affect the Pinia store"],"tags":["browser-automation","spa","feed"],"backgroundTag":"spa-hydration-not-ready","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}