{"record":{"id":"2ce136f9b8cb5fdf","repo":"jackwener/OpenCLI","slug":"malformed-xiaohongshu-user-snapshot","errorCode":null,"errorMessage":"Malformed Xiaohongshu user snapshot","messagePattern":"Malformed Xiaohongshu user snapshot","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/user.js","lineNumber":43,"sourceCode":"      const loggedInVal = hasUserStore ? (userStore.loggedIn?._value ?? userStore.loggedIn) : undefined;\n      const pathName = (typeof location !== 'undefined' && location.pathname) ? location.pathname : '';\n      const onLoginPage = pathName.indexOf('/login') === 0;\n      return {\n        noteGroups: safeClone(rawNotes || []),\n        pageData: safeClone(rawPageData || {}),\n        storePresent: hasUserStore,\n        notesPresent: Array.isArray(rawNotes),\n        pageDataPresent: Boolean(rawPageData && typeof rawPageData === 'object' && Object.keys(rawPageData).length > 0),\n        loginWall: Boolean(onLoginPage || loggedInVal === false),\n      };\n    })()\n  `;\nasync function readUserSnapshot(page) {\n    return await page.evaluate(USER_SNAPSHOT_JS);\n}\nexport function assertReadableUserSnapshot(snapshot) {\n    if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) {\n        throw new CommandExecutionError('Malformed Xiaohongshu user snapshot');\n    }\n    if (snapshot.storePresent !== true) {\n        throw new CommandExecutionError('Malformed Xiaohongshu user snapshot: user store was not found');\n    }\n    if (snapshot.notesPresent !== true || !Array.isArray(snapshot.noteGroups)) {\n        throw new CommandExecutionError('Malformed Xiaohongshu user snapshot: notes array was not found');\n    }\n}\n/** 展平 noteGroups 后的真实笔记条数。小红书 user store 的 notes 是 [tab[], tab[], ...]\n *  形态（每个 tab 一个数组），首屏笔记在其中某个 tab 里；这里数所有 tab 里的笔记总数。 */\nexport function countFlatNotes(snapshot) {\n    const groups = snapshot?.noteGroups;\n    if (!Array.isArray(groups))\n        return 0;\n    let n = 0;\n    for (const g of groups)\n        n += Array.isArray(g) ? g.length : (g ? 1 : 0);\n    return n;","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/user.js#L25-L61","documentation":"assertReadableUserSnapshot validates the snapshot object returned by readUserSnapshot (which evaluates USER_SNAPSHOT_JS in the page). If the snapshot is not a plain object (null, undefined, or an array), the function throws this base CommandExecutionError. It means the in-page extraction script returned nothing usable at all — the page state could not even be read.","triggerScenarios":"page.evaluate returned null/undefined because USER_SNAPSHOT_JS crashed or returned nothing; the snapshot serialization failed; or the script returned an array instead of an object.","commonSituations":"Page navigated away or was blocked before evaluate ran; bot-detection served a challenge page with no app state; the evaluate result was not structured-cloneable and came back null.","solutions":["Re-run after waiting for page hydration (user store is injected asynchronously after page.goto)","Check that the page actually loaded xiaohongshu.com content and not a login/challenge/captcha page","Log the raw evaluate result before assertion to see what USER_SNAPSHOT_JS actually returned","Add a hydration wait before readUserSnapshot, as done in note.js/download.js"],"exampleFix":"// before\nconst snapshot = await readUserSnapshot(page);\nassertReadableUserSnapshot(snapshot);\n// after\nawait page.wait({ time: 2 });\nconst snapshot = await readUserSnapshot(page);\nassertReadableUserSnapshot(snapshot);","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isUserSnapshot(s) {\n  return Boolean(s && typeof s === 'object' && !Array.isArray(s) && s.storePresent === true);\n}\n// usage\nconst snapshot = await readUserSnapshot(page);\nif (!isUserSnapshot(snapshot)) await page.reload(); // before asserting","tryCatchPattern":"try {\n  assertReadableUserSnapshot(snapshot);\n} catch (err) {\n  if (/Malformed Xiaohongshu user snapshot$/.test(err.message)) {\n    await page.reload(); const snapshot2 = await readUserSnapshot(page); assertReadableUserSnapshot(snapshot2);\n  } else throw err;\n}","preventionTips":["Log the raw evaluate result when snapshots come back unusable","Ensure the page finished loading before snapshotting","Detect login/challenge pages before running the snapshot script"],"tags":["browser-automation","snapshot","hydration","xiaohongshu"],"backgroundTag":"malformed-page-snapshot","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}