jackwener/OpenCLI · error · CommandExecutionError

xiaohongshu collection DOM extraction returned malformed row

Error message

xiaohongshu collection DOM extraction returned malformed rows

What it means

extractNotesFromDom runs the EXTRACT_COLLECTION_DOM_JS script in the page and expects a JSON array back. If page.evaluate resolves to anything other than an array, the DOM extraction layer is considered broken and a CommandExecutionError is thrown.

Source

Thrown at clis/xiaohongshu/collection-helpers.js:234

    await page.goto('https://www.xiaohongshu.com/explore');
    await page.wait(2);
    await throwIfLoginWall(page);
    const userId = unwrapBrowserResult(await page.evaluate(`() => {
      const user = window.__INITIAL_STATE__?.user?.userInfo;
      const info = user?._value ?? user ?? {};
      return info.user_id || info.userId || info.userID || '';
    }`));
    const clean = toCleanString(userId);
    if (!clean) {
        throw new AuthRequiredError('www.xiaohongshu.com', 'Not logged into Xiaohongshu (could not resolve current user id)');
    }
    return clean;
}

export async function extractNotesFromDom(page) {
    const payload = unwrapBrowserResult(await page.evaluate(EXTRACT_COLLECTION_DOM_JS));
    if (!Array.isArray(payload)) {
        throw new CommandExecutionError('xiaohongshu collection DOM extraction returned malformed rows');
    }
    return payload.filter((item) => item?.id);
}

export async function fetchXhsCollectionNotes(page, {
    userId,
    profileTab,
    apiPattern,
    limit,
    emptyLabel,
}) {
    const capturedRequests = [];
    await page.installInterceptor(apiPattern);
    await page.goto(buildProfileCollectionUrl(userId, profileTab));
    await page.wait(2);
    await assertOnCollectionProfile(page, userId);
    let notes = [];
    for (let i = 0; i < 16; i++) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Ensure the collection page fully loaded (wait for the note grid selector) before calling extractNotesFromDom.
  2. Console-log the raw page.evaluate result to see what the script actually returned.
  3. Update EXTRACT_COLLECTION_DOM_JS to match the current XHS DOM structure.
  4. Add a null/undefined guard upstream or fall back to the API-based note fetch path.

Example fix

// before
if (!Array.isArray(payload)) throw new CommandExecutionError('...malformed rows');
// after
const raw = unwrapBrowserResult(await page.evaluate(EXTRACT_COLLECTION_DOM_JS));
if (!Array.isArray(payload)) {
  console.error('DOM extraction returned:', raw);
  throw new CommandExecutionError('...malformed rows');
}
Defensive patterns

Strategy: type-guard

Validate before calling

const raw = await page.evaluate(EXTRACT_COLLECTION_DOM_JS);
const payload = unwrapBrowserResult(raw);
if (!Array.isArray(payload)) throw new Error(`DOM extraction returned ${typeof payload}`);

Type guard

function isNoteRows(v) {
  return Array.isArray(v) && v.every(i => i == null || typeof i === 'object');
}

Try / catch

try {
  const rows = await extractNotesFromDom(page);
} catch (e) {
  if (e instanceof CommandExecutionError) {
    await page.waitForSelector('.note-item', { timeout: 10000 }).catch(() => {});
    return extractNotesFromDom(page);
  }
  throw e;
}

Prevention

When it happens

Trigger: The injected script returns undefined/null or an object because the collection DOM did not render, a selector/structure change broke extraction, or unwrapBrowserResult passed through a non-array value.

Common situations: XHS updated its DOM so the in-page script's try/catch returns a sentinel object, the collection page failed to load, or a CSP/anti-bot mechanism interfered with evaluate.

Understand the failure class

Related errors


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