{"record":{"id":"82c5ec5d07853386","repo":"jackwener/OpenCLI","slug":"note-comments-require-login","errorCode":null,"errorMessage":"Note comments require login","messagePattern":"Note comments require login","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/comments.js","lineNumber":315,"sourceCode":"    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) : '',\n            profileUrl: c.authorHrefRaw ? buildXhsProfileUrl(c.authorHrefRaw) : '',\n            text: c.text,\n            likes: c.likes,\n            time: c.time,\n            is_reply: c.is_reply,\n            reply_to: c.reply_to,\n            images: c.images ?? [],\n        });","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/comments.js#L297-L333","documentation":"The extractor sets data.loginWall when the note detail page requires authentication to view comments. The command converts this into AuthRequiredError for www.xiaohongshu.com with the message 'Note comments require login', telling callers to authenticate the browser session.","triggerScenarios":"page.evaluate returns { loginWall: true } — the page rendered a login prompt/redirect instead of the comment list, typically for notes whose comments are members-only or when the session is anonymous.","commonSituations":"Running without a logged-in session (no cookies persisted); Xiaohongshu now requires login for comment viewing where it previously did not (policy change); expired session cookies; accessing comments of restricted/PGC notes.","solutions":["Log in once so the session cookies persist, then rerun the command","Refresh/re-authenticate — cookies may have expired","Check whether the specific note requires membership or is region-restricted","Update the library if Xiaohongshu changed login-wall markup and the flag misfires"],"exampleFix":"// before\nconst cli = new XiaohongshuCli(); // anonymous session\nawait cli.comments(noteUrl);\n// after\nconst cli = new XiaohongshuCli({ userDataDir: './xhs-profile' });\nawait cli.login(); // persist cookies once\nawait cli.comments(noteUrl);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const comments = await cli.comments(noteUrl);\n} catch (err) {\n  if (err instanceof AuthRequiredError || /require login/i.test(err.message)) {\n    await cli.login(); // authenticate session then retry\n    return cli.comments(noteUrl);\n  }\n  throw err;\n}","preventionTips":["Log in once and persist cookies (userDataDir) before scraping","Refresh sessions before they expire","Skip notes known to require membership/login for comments","Watch for Xiaohongshu policy changes requiring login for comments"],"tags":["auth","login-required","scraping","session"],"backgroundTag":"auth-login-required","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}