jackwener/OpenCLI · error · CommandExecutionError

Malformed Xiaohongshu user snapshot

Error message

Malformed Xiaohongshu user snapshot

What it means

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.

Source

Thrown at clis/xiaohongshu/user.js:43

      const loggedInVal = hasUserStore ? (userStore.loggedIn?._value ?? userStore.loggedIn) : undefined;
      const pathName = (typeof location !== 'undefined' && location.pathname) ? location.pathname : '';
      const onLoginPage = pathName.indexOf('/login') === 0;
      return {
        noteGroups: safeClone(rawNotes || []),
        pageData: safeClone(rawPageData || {}),
        storePresent: hasUserStore,
        notesPresent: Array.isArray(rawNotes),
        pageDataPresent: Boolean(rawPageData && typeof rawPageData === 'object' && Object.keys(rawPageData).length > 0),
        loginWall: Boolean(onLoginPage || loggedInVal === false),
      };
    })()
  `;
async function readUserSnapshot(page) {
    return await page.evaluate(USER_SNAPSHOT_JS);
}
export function assertReadableUserSnapshot(snapshot) {
    if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) {
        throw new CommandExecutionError('Malformed Xiaohongshu user snapshot');
    }
    if (snapshot.storePresent !== true) {
        throw new CommandExecutionError('Malformed Xiaohongshu user snapshot: user store was not found');
    }
    if (snapshot.notesPresent !== true || !Array.isArray(snapshot.noteGroups)) {
        throw new CommandExecutionError('Malformed Xiaohongshu user snapshot: notes array was not found');
    }
}
/** 展平 noteGroups 后的真实笔记条数。小红书 user store 的 notes 是 [tab[], tab[], ...]
 *  形态(每个 tab 一个数组),首屏笔记在其中某个 tab 里;这里数所有 tab 里的笔记总数。 */
export function countFlatNotes(snapshot) {
    const groups = snapshot?.noteGroups;
    if (!Array.isArray(groups))
        return 0;
    let n = 0;
    for (const g of groups)
        n += Array.isArray(g) ? g.length : (g ? 1 : 0);
    return n;

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run after waiting for page hydration (user store is injected asynchronously after page.goto)
  2. Check that the page actually loaded xiaohongshu.com content and not a login/challenge/captcha page
  3. Log the raw evaluate result before assertion to see what USER_SNAPSHOT_JS actually returned
  4. Add a hydration wait before readUserSnapshot, as done in note.js/download.js

Example fix

// before
const snapshot = await readUserSnapshot(page);
assertReadableUserSnapshot(snapshot);
// after
await page.wait({ time: 2 });
const snapshot = await readUserSnapshot(page);
assertReadableUserSnapshot(snapshot);
Defensive patterns

Strategy: type-guard

Type guard

function isUserSnapshot(s) {
  return Boolean(s && typeof s === 'object' && !Array.isArray(s) && s.storePresent === true);
}
// usage
const snapshot = await readUserSnapshot(page);
if (!isUserSnapshot(snapshot)) await page.reload(); // before asserting

Try / catch

try {
  assertReadableUserSnapshot(snapshot);
} catch (err) {
  if (/Malformed Xiaohongshu user snapshot$/.test(err.message)) {
    await page.reload(); const snapshot2 = await readUserSnapshot(page); assertReadableUserSnapshot(snapshot2);
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/2ce136f9b8cb5fdf. Report an issue: GitHub.