{"record":{"id":"400ac45f4159ebe7","repo":"jackwener/OpenCLI","slug":"unexpected-evaluate-response","errorCode":null,"errorMessage":"Unexpected evaluate response","messagePattern":"Unexpected evaluate response","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/comments.js","lineNumber":307,"sourceCode":"    domain: 'www.xiaohongshu.com',\n    strategy: Strategy.COOKIE,\n    navigateBefore: false,\n    args: [\n        { name: 'note-id', required: true, positional: true, help: 'Full Xiaohongshu note URL with xsec_token' },\n        { name: 'limit', type: 'int', default: 20, help: 'Number of top-level comments (max 50)' },\n        { name: 'with-replies', type: 'boolean', default: false, help: 'Include nested replies; reply_to is the direct target shown by the page' },\n    ],\n    columns: ['rank', 'author', 'userId', 'profileUrl', 'text', 'likes', 'time', 'is_reply', 'reply_to', 'images'],\n    func: async (page, kwargs) => {\n        const limit = parseCommentLimit(kwargs.limit);\n        const withReplies = Boolean(kwargs['with-replies']);\n        const raw = String(kwargs['note-id']);\n        const noteId = parseNoteId(raw);\n        await page.goto(buildNoteUrl(raw, { commandName: 'xiaohongshu comments' }));\n        await page.wait({ time: 2 + Math.random() * 3 });\n        const data = await page.evaluate(buildCommentsExtractJs(withReplies, limit));\n        if (!data || typeof data !== 'object') {\n            throw new EmptyResultError('xiaohongshu/comments', 'Unexpected evaluate response');\n        }\n        if (data.securityBlock) {\n            throw new CliError('SECURITY_BLOCK', 'Xiaohongshu security block: the note detail page was blocked by risk control.', /^https?:\\/\\//.test(raw)\n                ? 'The page may be temporarily restricted. Try again later or from a different session.'\n                : 'Try using a full URL from search results (with xsec_token) instead of a bare note ID.');\n        }\n        if (data.loginWall) {\n            throw new AuthRequiredError('www.xiaohongshu.com', 'Note comments require login');\n        }\n        // noteId currently unused after parsing — kept for symmetry with the note command\n        void noteId;\n        const all = normalizeCommentRows(data.results, 'xiaohongshu/comments');\n        // authorHrefRaw is a raw transport field from the extractor; it is consumed\n        // here into userId / profileUrl and intentionally not part of the row shape.\n        const enrich = (c, i) => ({\n            rank: i + 1,\n            author: c.author,\n            userId: c.authorHrefRaw ? parseXhsProfileHref(c.authorHrefRaw) : '',","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/comments.js#L289-L325","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry the command — transient render timing is the most common cause","Increase the wait time / add an explicit wait for the comment container selector before evaluating","Update buildCommentsExtractJs to match the current page structure and ensure it always returns an object (even an empty one)","Log the raw evaluate result to confirm whether the script is throwing internally"],"exampleFix":"// before\nconst data = await page.evaluate(buildCommentsExtractJs(withReplies, limit));\nif (!data || typeof data !== 'object') {\n    throw new EmptyResultError('xiaohongshu/comments', 'Unexpected evaluate response');\n}\n// after\nawait page.waitForSelector('.comments-container', { timeout: 15000 }).catch(() => {});\nconst data = await page.evaluate(buildCommentsExtractJs(withReplies, limit));\nif (!data || typeof data !== 'object') {\n    throw new EmptyResultError('xiaohongshu/comments', 'Unexpected evaluate response (page may not have rendered comments)');\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":"const isEvalPayload = (v) => v !== null && typeof v === 'object';","tryCatchPattern":"try {\n  const comments = await cli.comments(noteUrl);\n} catch (err) {\n  if (err.message === 'Unexpected evaluate response') {\n    await sleep(5000); // retry after render delay\n    return cli.comments(noteUrl);\n  }\n  throw err;\n}","preventionTips":["Wait for comment container selectors before evaluating","Ensure the extract script always returns an object even on partial failure","Increase page wait times on slow networks","Catch in-page script errors and surface them as objects"],"tags":["scraping","empty-result","dom-changes","puppeteer"],"backgroundTag":"empty-scrape-result","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}