jackwener/OpenCLI · error · CommandExecutionError

${webHost} feed: feed item ${id} is missing xsecToken; canno

Error message

${webHost} feed: feed item ${id} is missing xsecToken; cannot build a signed drill-down URL

What it means

Feed items also require an xsecToken; runFeed throws this error when toCleanString(row.xsecToken) is empty, because signed drill-down URLs for note detail cannot be constructed without it.

Source

Thrown at clis/xiaohongshu/feed.js:126

        throw new CommandExecutionError(`${webHost} feed: ${data.error}`, `The SPA may still be hydrating; reload ${webHost}/explore and retry.`);
    }
    if (!Array.isArray(data.items)) {
        throw new CommandExecutionError(`${webHost} feed: unexpected items payload shape`);
    }
    const rows = [];
    for (const row of data.items) {
        if (rows.length >= limit)
            break;
        if (!row || typeof row !== 'object') {
            throw new CommandExecutionError(`${webHost} feed: malformed feed item`);
        }
        const id = toCleanString(row.id);
        if (!id) {
            throw new CommandExecutionError(`${webHost} feed: feed item is missing note id`);
        }
        const xsecToken = toCleanString(row.xsecToken);
        if (!xsecToken) {
            throw new CommandExecutionError(`${webHost} feed: feed item ${id} is missing xsecToken; cannot build a signed drill-down URL`);
        }
        rows.push({
            id,
            title: toCleanString(row.title),
            type: toCleanString(row.type),
            author: toCleanString(row.author),
            likes: toCleanString(row.likes),
            url: buildFeedNoteUrl(webHost, id, xsecToken),
        });
    }
    if (rows.length === 0) {
        throw new EmptyResultError(`${webHost}/feed`, 'No feed items in the hydrated store.');
    }
    return rows;
}

export const command = cli({
    site: 'xiaohongshu',

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Inspect the row in DevTools to find the current token field name and update FEEDS_READ_JS/mapping (e.g. row.xsec_token)
  2. Retry after full hydration so tokens are populated
  3. Skip token-less items and build URLs only for complete rows
  4. Re-fetch the feed via the same explore entry point that includes xsecToken

Example fix

// before
const xsecToken = toCleanString(row.xsecToken);
// after (tolerate alternate key)
const xsecToken = toCleanString(row.xsecToken ?? row.xsec_token);
Defensive patterns

Strategy: validation

Validate before calling

const drillable = (data.items ?? []).filter(r => r && toCleanString(r.xsecToken ?? r.xsec_token) && toCleanString(r.id));

Type guard

function hasXsecToken(row) { return !!row && typeof row === 'object' && Boolean(toCleanString(row.xsecToken ?? row.xsec_token)); }

Try / catch

try {
  const rows = await runFeed(page, webHost, limit);
} catch (err) {
  if (err instanceof CommandExecutionError && err.message.includes('xsecToken')) {
    // skip token-less items or re-fetch feed from the entry point that issues tokens
  } else throw err;
}

Prevention

When it happens

Trigger: A row has a valid id but no xsecToken field (empty/undefined), commonly when the feed payload variant differs (e.g. different entry point or a site change moving the token).

Common situations: Site changes renaming xsecToken (e.g. xsec_token), feeds fetched from a context that doesn't issue tokens, promoted cards lacking tokens, partial hydration dropping the token field.

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/bcdf41e94a6352af. Report an issue: GitHub.