jackwener/OpenCLI · error · CommandExecutionError

xiaohongshu collection API returned a note without stable id

Error message

xiaohongshu collection API returned a note without stable id/xsec token

What it means

Each mapped note row must have a stable id and a URL containing xsec_token, which the library needs to open notes reliably later. If mapCollectionNote yields a row missing id or lacking an xsec_token query parameter, CommandExecutionError is thrown. This prevents emitting rows that would fail on subsequent detail-page access.

Source

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

export function extractNotesFromResponses(requests, fallbackUserId) {
    const rows = [];
    const seen = new Set();
    for (const req of requests ?? []) {
        const payload = unwrapBrowserResult(req);
        if (!isObject(payload)) {
            throw new CommandExecutionError('xiaohongshu collection API returned a malformed payload');
        }
        const data = payload.data;
        if (!isObject(data)) {
            throw new CommandExecutionError('xiaohongshu collection API returned malformed data');
        }
        const notes = data.notes ?? data.note_list;
        if (!Array.isArray(notes))
            throw new CommandExecutionError('xiaohongshu collection API returned malformed notes');
        for (const entry of notes) {
            const row = mapCollectionNote(entry, { fallbackUserId });
            if (!row?.id || !row.url.includes('xsec_token=')) {
                throw new CommandExecutionError('xiaohongshu collection API returned a note without stable id/xsec token');
            }
            if (seen.has(row.id))
                continue;
            seen.add(row.id);
            rows.push(row);
        }
    }
    return rows;
}

export const EXTRACT_COLLECTION_DOM_JS = `
  (() => {
    const normalizeUrl = (href) => {
      if (!href) return '';
      let url;
      try {
        url = new URL(href, 'https://www.xiaohongshu.com');
      } catch {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Skip/drop offending entries instead of aborting: pre-filter notes whose entry lacks id or xsec_token before calling extractNotesFromResponses
  2. Refresh login — restricted visibility can hide fields
  3. Inspect the raw entry JSON to find where id/xsec_token now live and update mapping
  4. Retry later if entries were transiently incomplete

Example fix

// before
const rows = extractNotesFromResponses(reqs, userId); // throws on one bad entry
// after
const cleaned = reqs.map(req => {
  const p = req.json;
  const notes = p?.data?.notes ?? p?.data?.note_list ?? [];
  return { ...p, data: { ...p.data, notes: notes.filter(n => n.id && String(n.url ?? '').includes('xsec_token=')) } };
});
const rows = extractNotesFromResponses(cleaned, userId);
Defensive patterns

Strategy: validation

Validate before calling

const usable = (entry) => entry && (entry.id ?? entry.note_id) && String(entry.url ?? entry.note_url ?? '').includes('xsec_token=');
// pre-filter entries before extraction so one bad note doesn't abort the run

Type guard

const hasStableIdAndToken = (row) => typeof row?.id === 'string' && row.id.length > 0 && typeof row.url === 'string' && row.url.includes('xsec_token=');

Try / catch

try { rows = extractNotesFromResponses(reqs, userId); } catch (e) { if (String(e.message).includes('xsec token')) { console.warn('skipping notes missing xsec_token'); rows = partialExtract(reqs, userId); } else throw e; }

Prevention

When it happens

Trigger: A note entry in the API response lacks its note_id or its url/xsec_token field — e.g. deleted/hidden notes, partially populated entries, or a mapping change in mapCollectionNote's source fields.

Common situations: Scraping a collection containing notes the session cannot fully see (restricted or removed notes); xiaohongshu omitting xsec_token on some list entries; version change in the entry schema (e.g. nested note objects).

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


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