jackwener/OpenCLI · error · CliError

SECURITY_BLOCK

SECURITY_BLOCK

Error message

Xiaohongshu security block: the note detail page was blocked by risk control.

What it means

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.

Source

Thrown at clis/xiaohongshu/comments.js:310

    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) : '',
            profileUrl: c.authorHrefRaw ? buildXhsProfileUrl(c.authorHrefRaw) : '',
            text: c.text,
            likes: c.likes,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. If you passed a bare note ID, switch to the full note URL from search results including xsec_token
  2. Wait and retry later, or use a different session/IP (the error hint suggests both)
  3. Slow down request rate, add randomized waits, and reuse a warmed-up logged-in session
  4. Use a less suspicious browser profile (real user agent, headful mode, residential proxy)

Example fix

// before
await cli comments '661a1b2c000000002203a1f5' --limit 20;
// after
await cli comments 'https://www.xiaohongshu.com/explore/661a1b2c000000002203a1f5?xsec_token=AB...' --limit 20;
Defensive patterns

Strategy: fallback

Try / catch

try {
  const comments = await cli.comments(noteUrl);
} catch (err) {
  if (err.code === 'SECURITY_BLOCK') {
    // rotate session/IP, back off, or retry with full URL incl. xsec_token
  } else throw err;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


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