{"record":{"id":"8812379ee259345e","repo":"jackwener/OpenCLI","slug":"security-block","errorCode":"SECURITY_BLOCK","errorMessage":"Xiaohongshu security block: the note detail page was blocked by risk control.","messagePattern":"Xiaohongshu security block: the note detail page was blocked by risk control\\.","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/comments.js","lineNumber":310,"sourceCode":"    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) : '',\n            profileUrl: c.authorHrefRaw ? buildXhsProfileUrl(c.authorHrefRaw) : '',\n            text: c.text,\n            likes: c.likes,","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/comments.js#L292-L328","documentation":"The comments extractor flags data.securityBlock when the note detail page was intercepted by Xiaohongshu's risk-control system instead of showing the note. The command surfaces this as a CliError with code SECURITY_BLOCK and a hint that differs depending on whether the caller passed a full URL or a bare note ID.","triggerScenarios":"page.evaluate returns { securityBlock: true } — the rendered page matched the extractor's risk-control/captcha detection (verify page, slider captcha, or an 'environment abnormal' interstitial).","commonSituations":"Scraping many notes from one session/IP triggers rate limiting; datacenter IP or headless browser fingerprint flagged; bare note ID without xsec_token is rejected by risk control more aggressively; stale session cookies.","solutions":["If you passed a bare note ID, switch to the full note URL from search results including xsec_token","Wait and retry later, or use a different session/IP (the error hint suggests both)","Slow down request rate, add randomized waits, and reuse a warmed-up logged-in session","Use a less suspicious browser profile (real user agent, headful mode, residential proxy)"],"exampleFix":"// before\nawait cli comments '661a1b2c000000002203a1f5' --limit 20;\n// after\nawait cli comments 'https://www.xiaohongshu.com/explore/661a1b2c000000002203a1f5?xsec_token=AB...' --limit 20;","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const comments = await cli.comments(noteUrl);\n} catch (err) {\n  if (err.code === 'SECURITY_BLOCK') {\n    // rotate session/IP, back off, or retry with full URL incl. xsec_token\n  } else throw err;\n}","preventionTips":["Always pass full URLs from search results including xsec_token","Throttle request rate and randomize waits between notes","Use residential IPs and realistic browser fingerprints","Reuse a warmed, logged-in session instead of fresh anonymous ones"],"tags":["anti-bot","rate-limit","scraping","security-block"],"backgroundTag":"anti-bot-security-block","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}