DIYgod/RSSHub · error · CaptchaError

小红书风控校验,请稍后再试

Error message

小红书风控校验,请稍后再试

What it means

CaptchaError raised during the Playwright user-profile fetch when the page contains a .fe-verify-box element — Xiaohongshu's anti-bot verification widget. RSSHub classifies this as a distinct CaptchaError so upstream mapping can treat it as retryable rather than a hard failure.

Source

Thrown at lib/routes/xiaohongshu/util.ts:88

                        const request = route.request();
                        request.resourceType() === 'document' || request.resourceType() === 'script' || request.resourceType() === 'xhr' || request.resourceType() === 'other' ? route.continue() : route.abort();
                    });
                },
            });
            try {
                let collect = '';
                logger.http(`Requesting ${url}`);
                await page.goto(url, {
                    waitUntil: 'domcontentloaded',
                });
                try {
                    await page.waitForSelector('div.reds-tab-item:nth-child(2), .fe-verify-box', { timeout: 3000 });
                } catch {
                    //
                }

                if (await page.$('.fe-verify-box')) {
                    throw new CaptchaError('小红书风控校验,请稍后再试');
                }

                const content = await page.content();
                const initialState = JSON.parse(extractInitialState(load(content)));

                if (!(await page.$('.lock-icon'))) {
                    try {
                        await page.click('div.reds-tab-item:nth-child(2)', { timeout: 3000 });
                        const response = await page.waitForResponse(
                            (res) => {
                                const req = res.request();
                                return req.url().includes('/api/sns/web/v2/note/collect/page') && req.method() === 'GET' && req.resourceType() === 'xhr';
                            },
                            { timeout: 5000 }
                        );
                        collect = await response.json();
                    } catch {
                        //

View on GitHub (pinned to bed535e087)

Solutions

  1. Provide a valid XIAOHONGSHU_COOKIE (config.xiaohongshu.cookie) so the session is already trusted and the verify box is not triggered.
  2. Slow down polling of xiaohongshu routes and let config.cache.routeExpire absorb repeats.
  3. Route egress through a residential/proxy IP (config.xiaohongshu.proxy) less likely to be challenged, and retry after a backoff.
Defensive patterns

Strategy: retry

Validate before calling

// Skip the Playwright path entirely if a trusted cookie is available
if (!config.xiaohongshu.cookie) {
    console.warn('No XIAOHONGSHU_COOKIE set — xiaohongshu will likely trigger captcha (.fe-verify-box).');
}

Type guard

function isXiaohongshuCaptchaError(e: unknown): boolean {
    return e instanceof Error && e.name === 'CaptchaError';
}

Try / catch

for (const backoff of [0, 30_000, 120_000]) {
    try {
        return await getUser(url, cache);
    } catch (e) {
        if (!isXiaohongshuCaptchaError(e) || backoff === 120_000) throw e;
        await new Promise((r) => setTimeout(r, backoff));
    }
}

Prevention

When it happens

Trigger: After page.goto(url) and waitForSelector for the tab or the verify box, page.$('.fe-verify-box') resolves truthy at util.ts:87-89, meaning Xiaohongshu challenged the headless browser.

Common situations: High request volume from the RSSHub host IP, datacenter IP reputation, missing/warm XIAOHONGSHU_COOKIE, or running Playwright without enough human-like signals.

Related errors


AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12). Data as JSON: /api/errors/7094ffb25cd2be37. Report an issue: GitHub.