jackwener/OpenCLI · error · EmptyResultError

Unexpected evaluate response

Error message

Unexpected evaluate response

What it means

After loading the note page, the command calls page.evaluate(buildCommentsExtractJs(...)) and expects a non-null object back. EmptyResultError is thrown when the evaluated script returns nothing usable (null, undefined, or a primitive), meaning the extraction script did not produce a result payload.

Source

Thrown at clis/xiaohongshu/comments.js:307

    domain: 'www.xiaohongshu.com',
    strategy: Strategy.COOKIE,
    navigateBefore: false,
    args: [
        { name: 'note-id', required: true, positional: true, help: 'Full Xiaohongshu note URL with xsec_token' },
        { name: 'limit', type: 'int', default: 20, help: 'Number of top-level comments (max 50)' },
        { name: 'with-replies', type: 'boolean', default: false, help: 'Include nested replies; reply_to is the direct target shown by the page' },
    ],
    columns: ['rank', 'author', 'userId', 'profileUrl', 'text', 'likes', 'time', 'is_reply', 'reply_to', 'images'],
    func: async (page, kwargs) => {
        const limit = parseCommentLimit(kwargs.limit);
        const withReplies = Boolean(kwargs['with-replies']);
        const raw = String(kwargs['note-id']);
        const noteId = parseNoteId(raw);
        await page.goto(buildNoteUrl(raw, { commandName: 'xiaohongshu comments' }));
        await page.wait({ time: 2 + Math.random() * 3 });
        const data = await page.evaluate(buildCommentsExtractJs(withReplies, limit));
        if (!data || typeof data !== 'object') {
            throw new EmptyResultError('xiaohongshu/comments', 'Unexpected evaluate response');
        }
        if (data.securityBlock) {
            throw new CliError('SECURITY_BLOCK', 'Xiaohongshu security block: the note detail page was blocked by risk control.', /^https?:\/\//.test(raw)
                ? 'The page may be temporarily restricted. Try again later or from a different session.'
                : 'Try using a full URL from search results (with xsec_token) instead of a bare note ID.');
        }
        if (data.loginWall) {
            throw new AuthRequiredError('www.xiaohongshu.com', 'Note comments require login');
        }
        // noteId currently unused after parsing — kept for symmetry with the note command
        void noteId;
        const all = normalizeCommentRows(data.results, 'xiaohongshu/comments');
        // authorHrefRaw is a raw transport field from the extractor; it is consumed
        // here into userId / profileUrl and intentionally not part of the row shape.
        const enrich = (c, i) => ({
            rank: i + 1,
            author: c.author,
            userId: c.authorHrefRaw ? parseXhsProfileHref(c.authorHrefRaw) : '',

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry the command — transient render timing is the most common cause
  2. Increase the wait time / add an explicit wait for the comment container selector before evaluating
  3. Update buildCommentsExtractJs to match the current page structure and ensure it always returns an object (even an empty one)
  4. Log the raw evaluate result to confirm whether the script is throwing internally

Example fix

// before
const data = await page.evaluate(buildCommentsExtractJs(withReplies, limit));
if (!data || typeof data !== 'object') {
    throw new EmptyResultError('xiaohongshu/comments', 'Unexpected evaluate response');
}
// after
await page.waitForSelector('.comments-container', { timeout: 15000 }).catch(() => {});
const data = await page.evaluate(buildCommentsExtractJs(withReplies, limit));
if (!data || typeof data !== 'object') {
    throw new EmptyResultError('xiaohongshu/comments', 'Unexpected evaluate response (page may not have rendered comments)');
}
Defensive patterns

Strategy: retry

Type guard

const isEvalPayload = (v) => v !== null && typeof v === 'object';

Try / catch

try {
  const comments = await cli.comments(noteUrl);
} catch (err) {
  if (err.message === 'Unexpected evaluate response') {
    await sleep(5000); // retry after render delay
    return cli.comments(noteUrl);
  }
  throw err;
}

Prevention

When it happens

Trigger: The in-page extract script threw internally and the evaluate wrapper swallowed it into null/undefined; the page navigated away or the script ran before comment data existed; page.evaluate returned a non-object primitive.

Common situations: Slow network so comments never rendered within the wait window; Xiaohongshu served an unexpected page variant (redirect, region page) where the script exits early; an outdated extract script incompatible with the current DOM.

Related errors


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