jackwener/OpenCLI · error · TimeoutError

xiaohongshu search content

Error message

xiaohongshu search content

What it means

The `xiaohongshu search` command opens a headless browser, navigates to xiaohongshu.com/search_result, and waits up to CONTENT_WAIT_SECONDS for search results to render. The browser-side wait script returns 'timeout' when the expected content never appears, and collectSearchHarvest translates that into a TimeoutError. This usually means the page rendered something other than the expected search feed (login wall, security check, blank/collapsed render) or the site DOM/behavior changed.

Source

Thrown at clis/xiaohongshu/search.js:795

            stopReason,
            elapsedMs,
            securityBlock,
          },
        };
      })()
    `;
}

async function collectSearchHarvest(page, limit, requestedFilters) {
    const waitResult = unwrapEvaluateResult(await page.evaluate(WAIT_FOR_CONTENT_JS));
    if (waitResult === 'login_wall') {
        throw new AuthRequiredError('www.xiaohongshu.com', 'Xiaohongshu search results are blocked behind a login wall');
    }
    if (waitResult === 'security_block') {
        throw new CliError('SECURITY_BLOCK', 'Xiaohongshu search was blocked by request-frequency or security controls.', 'Wait before retrying or use a different logged-in browser session.');
    }
    if (waitResult === 'timeout') {
        throw new TimeoutError('xiaohongshu search content', CONTENT_WAIT_SECONDS);
    }
    if (waitResult !== 'content') {
        throw new CommandExecutionError('Unexpected Xiaohongshu search wait payload shape.');
    }
    requireFilterApplication(await page.evaluate(buildApplySearchFiltersJs(requestedFilters)));
    const harvestOptions = harvestOptionsForLimit(limit);
    const harvest = requireHarvestPayload(await page.evaluate(buildScrollHarvestJs('www.xiaohongshu.com', limit, harvestOptions)), 'www.xiaohongshu.com');
    if (harvest.diag.securityBlock) {
        throw new CliError('SECURITY_BLOCK', 'Xiaohongshu search was blocked by request-frequency or security controls.', 'Wait before retrying or use a different logged-in browser session.');
    }
    return harvest;
}

async function replaceCollapsedTab(page, url) {
    if (typeof page.getActivePage !== 'function' || typeof page.newTab !== 'function' ||
        typeof page.setActivePage !== 'function' || typeof page.selectTab !== 'function' ||
        typeof page.closeTab !== 'function') {
        throw new CommandExecutionError(

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command once — transient slow rendering is the most common cause.
  2. Wait longer between invocations or run at an off-peak time so Xiaohongshu serves results without throttling.
  3. Verify in a real browser that the same search URL renders normal results; if a login wall or security banner shows, authenticate the browser session (cookies) first.
  4. If it reproduces consistently, the site layout likely changed — update the wait/content detection selectors in WAIT_FOR_CONTENT_JS.

Example fix

// before
await page.goto(url);
let harvest = await collectSearchHarvest(page, limit, requestedFilters);
// after: retry the harvest once before surfacing the timeout
let harvest;
try {
  harvest = await collectSearchHarvest(page, limit, requestedFilters);
} catch (err) {
  if (err instanceof TimeoutError) {
    harvest = await collectSearchHarvest(page, limit, requestedFilters);
  } else { throw err; }
}
Defensive patterns

Strategy: retry

Validate before calling

// Probe reachability before invoking
const res = await fetch('https://www.xiaohongshu.com/search_result?keyword=test', { method: 'HEAD' });
if (res.status === 403 || res.status === 429) throw new Error('Site is throttling; back off before searching');

Type guard

function isTimeout(err) { return err instanceof TimeoutError || err?.name === 'TimeoutError'; }

Try / catch

try {
  rows = await cli('xiaohongshu search', query);
} catch (err) {
  if (isTimeout(err)) {
    await sleep(BACKOFF_MS);           // let slow render / throttle pass
    rows = await cli('xiaohongshu search', query); // retry once
  } else { throw err; }
}

Prevention

When it happens

Trigger: page.evaluate(WAIT_FOR_CONTENT_JS) in collectSearchHarvest (clis/xiaohongshu/search.js:787) returns the sentinel 'timeout' — i.e. no search-result cards, login wall, or security banner appeared within the fixed wait window after page.goto(url). Any slow network, heavy anti-bot interstitial, or an unexpected DOM layout triggers it.

Common situations: Slow or proxied network connections where lazy-loaded results exceed CONTENT_WAIT_SECONDS; Xiaohongshu showing an A/B-tested or redesigned layout the wait script does not recognize; heavy anti-bot challenge pages; very obscure queries rendering a near-empty feed the detector misses.

Related errors


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