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

A CliError with code SECURITY_BLOCK thrown when the in-page extraction script (buildDownloadExtractJs) reports `securityBlock: true`, meaning Xiaohongshu risk control intercepted the note detail page (captcha, login wall, or block page) instead of returning note content. The library detects this marker and refuses to continue so it does not scrape a block page. The remediation hint depends on whether the input was a full URL or a bare note ID.

Source

Thrown at clis/xiaohongshu/download.js:226

    access: 'read',
    description: '下载小红书笔记中的图片和视频',
    domain: 'www.xiaohongshu.com',
    strategy: Strategy.COOKIE,
    navigateBefore: false,
    args: [
        { name: 'note-id', positional: true, required: true, help: 'Full Xiaohongshu note URL with xsec_token, or xhslink short link' },
        { name: 'output', default: './xiaohongshu-downloads', help: 'Output directory' },
    ],
    columns: ['index', 'type', 'status', 'size'],
    func: async (page, kwargs) => {
        const rawInput = String(kwargs['note-id']);
        const output = kwargs.output;
        const noteId = parseNoteId(rawInput);
        await page.goto(buildNoteUrl(rawInput, { allowShortLink: true, commandName: 'xiaohongshu download' }));
        await page.wait({ time: 1 + Math.random() * 2 });
        const data = await page.evaluate(buildDownloadExtractJs(noteId));
        if (data?.securityBlock) {
            throw new CliError('SECURITY_BLOCK', 'Xiaohongshu security block: the note detail page was blocked by risk control.', /^https?:\/\//.test(rawInput)
                ? '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 || typeof data !== 'object' || !Array.isArray(data.media)) {
            throw new CommandExecutionError('Xiaohongshu media extraction returned malformed payload.');
        }
        if (data.media.length === 0) {
            throw new EmptyResultError('xiaohongshu download', 'No downloadable media found on this note.');
        }
        // Extract cookies for authenticated downloads
        const cookies = formatCookieHeader(await page.getCookies({ domain: 'xiaohongshu.com' }));
        const resolvedNoteId = typeof data.noteId === 'string' && data.noteId.trim()
            ? data.noteId.trim()
            : noteId;
        return downloadMedia(data.media, {
            output,
            subdir: resolvedNoteId,
            cookies,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Use the full note URL from search results including a fresh xsec_token instead of a bare note ID.
  2. Wait and retry later, or switch to a different session/account/IP.
  3. Slow down request rate between downloads (the code already waits 1-2s randomly; increase it).
  4. Log in again / refresh cookies if the session was invalidated.

Example fix

// before
await cli.run('xiaohongshu download', { input: '65f1abc123' });
// after
await cli.run('xiaohongshu download', { input: 'https://www.xiaohongshu.com/explore/65f1abc123?xsec_token=ABFreshToken' });
Defensive patterns

Strategy: try-catch

Validate before calling

// Prefer full URLs with xsec_token; detect bare IDs upfront
function needsXsecToken(input) {
  return !/^https?:\/\//.test(input) || !input.includes('xsec_token=');
}
if (needsXsecToken(input)) console.warn('bare note id / missing xsec_token — high security-block risk');

Type guard

function isSecurityBlock(err) { return err instanceof Error && err.code === 'SECURITY_BLOCK'; }

Try / catch

try {
  await cli.run('xiaohongshu download', { input: url });
} catch (err) {
  if (err.code === 'SECURITY_BLOCK') {
    await sleep(backoffMs); // rotate session/IP or wait, then retry
    return downloadNote(url);
  }
  throw err;
}

Prevention

When it happens

Trigger: Running `xiaohongshu download` with input that leads to a risk-controlled note detail page: a bare note ID without xsec_token, an expired/invalid xsec_token, too-frequent requests from one session/IP, or navigating to buildNoteUrl when the account is flagged.

Common situations: Scraping many notes in quick succession from the same session; using a bare note ID copied from elsewhere (missing the xsec_token query param); reusing an old share URL whose token expired; running from a datacenter IP that Xiaohongshu blocks.

Related errors


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