{"record":{"id":"ba944282db573a10","repo":"jackwener/OpenCLI","slug":"security-block-ba9442","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/download.js","lineNumber":226,"sourceCode":"    access: 'read',\n    description: '下载小红书笔记中的图片和视频',\n    domain: 'www.xiaohongshu.com',\n    strategy: Strategy.COOKIE,\n    navigateBefore: false,\n    args: [\n        { name: 'note-id', positional: true, required: true, help: 'Full Xiaohongshu note URL with xsec_token, or xhslink short link' },\n        { name: 'output', default: './xiaohongshu-downloads', help: 'Output directory' },\n    ],\n    columns: ['index', 'type', 'status', 'size'],\n    func: async (page, kwargs) => {\n        const rawInput = String(kwargs['note-id']);\n        const output = kwargs.output;\n        const noteId = parseNoteId(rawInput);\n        await page.goto(buildNoteUrl(rawInput, { allowShortLink: true, commandName: 'xiaohongshu download' }));\n        await page.wait({ time: 1 + Math.random() * 2 });\n        const data = await page.evaluate(buildDownloadExtractJs(noteId));\n        if (data?.securityBlock) {\n            throw new CliError('SECURITY_BLOCK', 'Xiaohongshu security block: the note detail page was blocked by risk control.', /^https?:\\/\\//.test(rawInput)\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 || typeof data !== 'object' || !Array.isArray(data.media)) {\n            throw new CommandExecutionError('Xiaohongshu media extraction returned malformed payload.');\n        }\n        if (data.media.length === 0) {\n            throw new EmptyResultError('xiaohongshu download', 'No downloadable media found on this note.');\n        }\n        // Extract cookies for authenticated downloads\n        const cookies = formatCookieHeader(await page.getCookies({ domain: 'xiaohongshu.com' }));\n        const resolvedNoteId = typeof data.noteId === 'string' && data.noteId.trim()\n            ? data.noteId.trim()\n            : noteId;\n        return downloadMedia(data.media, {\n            output,\n            subdir: resolvedNoteId,\n            cookies,","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/download.js#L208-L244","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use the full note URL from search results including a fresh xsec_token instead of a bare note ID.","Wait and retry later, or switch to a different session/account/IP.","Slow down request rate between downloads (the code already waits 1-2s randomly; increase it).","Log in again / refresh cookies if the session was invalidated."],"exampleFix":"// before\nawait cli.run('xiaohongshu download', { input: '65f1abc123' });\n// after\nawait cli.run('xiaohongshu download', { input: 'https://www.xiaohongshu.com/explore/65f1abc123?xsec_token=ABFreshToken' });","handlingStrategy":"try-catch","validationCode":"// Prefer full URLs with xsec_token; detect bare IDs upfront\nfunction needsXsecToken(input) {\n  return !/^https?:\\/\\//.test(input) || !input.includes('xsec_token=');\n}\nif (needsXsecToken(input)) console.warn('bare note id / missing xsec_token — high security-block risk');","typeGuard":"function isSecurityBlock(err) { return err instanceof Error && err.code === 'SECURITY_BLOCK'; }","tryCatchPattern":"try {\n  await cli.run('xiaohongshu download', { input: url });\n} catch (err) {\n  if (err.code === 'SECURITY_BLOCK') {\n    await sleep(backoffMs); // rotate session/IP or wait, then retry\n    return downloadNote(url);\n  }\n  throw err;\n}","preventionTips":["Always use full note URLs taken from search results, including a fresh xsec_token.","Throttle requests well below the built-in 1-2s wait; add jitter and longer delays for batch jobs.","Rotate sessions/accounts/IPs when downloading many notes.","Re-fetch URLs periodically — xsec_tokens expire."],"tags":["risk-control","anti-bot","security-block","xiaohongshu"],"backgroundTag":"security-block","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}