{"record":{"id":"62f7d7a21d5d9e75","repo":"jackwener/OpenCLI","slug":"xiaohongshu-search-content","errorCode":null,"errorMessage":"xiaohongshu search content","messagePattern":"xiaohongshu search content","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/search.js","lineNumber":795,"sourceCode":"            stopReason,\n            elapsedMs,\n            securityBlock,\n          },\n        };\n      })()\n    `;\n}\n\nasync function collectSearchHarvest(page, limit, requestedFilters) {\n    const waitResult = unwrapEvaluateResult(await page.evaluate(WAIT_FOR_CONTENT_JS));\n    if (waitResult === 'login_wall') {\n        throw new AuthRequiredError('www.xiaohongshu.com', 'Xiaohongshu search results are blocked behind a login wall');\n    }\n    if (waitResult === 'security_block') {\n        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.');\n    }\n    if (waitResult === 'timeout') {\n        throw new TimeoutError('xiaohongshu search content', CONTENT_WAIT_SECONDS);\n    }\n    if (waitResult !== 'content') {\n        throw new CommandExecutionError('Unexpected Xiaohongshu search wait payload shape.');\n    }\n    requireFilterApplication(await page.evaluate(buildApplySearchFiltersJs(requestedFilters)));\n    const harvestOptions = harvestOptionsForLimit(limit);\n    const harvest = requireHarvestPayload(await page.evaluate(buildScrollHarvestJs('www.xiaohongshu.com', limit, harvestOptions)), 'www.xiaohongshu.com');\n    if (harvest.diag.securityBlock) {\n        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.');\n    }\n    return harvest;\n}\n\nasync function replaceCollapsedTab(page, url) {\n    if (typeof page.getActivePage !== 'function' || typeof page.newTab !== 'function' ||\n        typeof page.setActivePage !== 'function' || typeof page.selectTab !== 'function' ||\n        typeof page.closeTab !== 'function') {\n        throw new CommandExecutionError(","sourceCodeStart":777,"sourceCodeEnd":813,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/search.js#L777-L813","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-run the command once — transient slow rendering is the most common cause.","Wait longer between invocations or run at an off-peak time so Xiaohongshu serves results without throttling.","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.","If it reproduces consistently, the site layout likely changed — update the wait/content detection selectors in WAIT_FOR_CONTENT_JS."],"exampleFix":"// before\nawait page.goto(url);\nlet harvest = await collectSearchHarvest(page, limit, requestedFilters);\n// after: retry the harvest once before surfacing the timeout\nlet harvest;\ntry {\n  harvest = await collectSearchHarvest(page, limit, requestedFilters);\n} catch (err) {\n  if (err instanceof TimeoutError) {\n    harvest = await collectSearchHarvest(page, limit, requestedFilters);\n  } else { throw err; }\n}","handlingStrategy":"retry","validationCode":"// Probe reachability before invoking\nconst res = await fetch('https://www.xiaohongshu.com/search_result?keyword=test', { method: 'HEAD' });\nif (res.status === 403 || res.status === 429) throw new Error('Site is throttling; back off before searching');","typeGuard":"function isTimeout(err) { return err instanceof TimeoutError || err?.name === 'TimeoutError'; }","tryCatchPattern":"try {\n  rows = await cli('xiaohongshu search', query);\n} catch (err) {\n  if (isTimeout(err)) {\n    await sleep(BACKOFF_MS);           // let slow render / throttle pass\n    rows = await cli('xiaohongshu search', query); // retry once\n  } else { throw err; }\n}","preventionTips":["Retry once with backoff before giving up; timeouts here are often transient render slowness.","Avoid running searches through slow proxies or heavy VPN tunnels.","Keep the browser session authenticated so login-wall interstitials don't stall rendering.","Watch for repeated timeouts from the same query — it may indicate a DOM redesign requiring a CLI update."],"tags":["timeout","scraping","browser-automation","xiaohongshu"],"backgroundTag":"page-load-timeout","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}