jackwener/OpenCLI · error · CommandExecutionError
Douyin search did not render result cards within the timeout
Error message
Douyin search did not render result cards within the timeout. Open the same search in Chrome and verify login/security state before retrying.
What it means
CommandExecutionError thrown when the evaluator reports state === 'timeout': the scroll-list result cards never appeared within RENDER_TIMEOUT_MS (15s). Unlike 'empty', the page never committed a result DOM at all, so the cause is usually rendering, network, or an anti-bot interstitial rather than a genuinely empty query.
Source
Thrown at clis/douyin/search.js:291
try {
result = unwrapEvaluateResult(await page.evaluate(WAIT_AND_EXTRACT_JS(RENDER_TIMEOUT_MS)));
} catch (error) {
throw new CommandExecutionError(`Douyin search extraction failed: ${error instanceof Error ? error.message : String(error)}`);
}
if (!result || typeof result !== 'object') {
throw new CommandExecutionError('Douyin search: unexpected evaluator payload shape');
}
if (result.state === 'login_wall') {
throw new AuthRequiredError(
'www.douyin.com',
'Douyin search results are blocked behind a login wall — log in at https://www.douyin.com in Chrome first.',
);
}
if (result.state === 'empty') {
throw new EmptyResultError('douyin search', `No Douyin videos matched "${keyword}".`);
}
if (result.state === 'timeout') {
throw new CommandExecutionError('Douyin search did not render result cards within the timeout. Open the same search in Chrome and verify login/security state before retrying.');
}
if (!Array.isArray(result.cards)) {
throw new CommandExecutionError('Douyin search: evaluator returned malformed cards payload');
}
if (result.cards.length === 0) {
throw new EmptyResultError('douyin search', `No Douyin videos matched "${keyword}".`);
}
const projected = projectSearchCards(result.cards, limit);
if (projected.invalidCount > 0) {
throw new CommandExecutionError('Douyin search parser found result cards without stable video url or description');
}
if (projected.rows.length === 0) {
throw new EmptyResultError('douyin search', `No Douyin videos matched "${keyword}".`);
}
return projected.rows;
},
});
View on GitHub (pinned to 49907e53dc)
Solutions
- Open the same search URL in the bound Chrome and check what actually rendered (captcha, blank page, results).
- If a captcha/security check appears, complete it in the browser and retry.
- Retry once — transient network slowness can exceed the 15s budget.
- If the markup changed, update the selector constants in clis/douyin/search.js to the new stable hooks.
Example fix
// operator workaround: retry with backoff
// before
const rows = await douyinSearch(keyword);
// after
import { CommandExecutionError } from '@jackwener/opencli/errors';
let rows;
for (let i = 0; i < 2 && !rows; i++) {
try { rows = await douyinSearch(keyword); }
catch (e) {
if (!(e instanceof CommandExecutionError) || !/timeout/.test(e.message)) throw e;
await new Promise(r => setTimeout(r, 5000));
}
} Defensive patterns
Strategy: retry
Try / catch
for (let attempt = 1; attempt <= 2; attempt++) {
try { return await douyinSearch(keyword); }
catch (e) {
if (attempt < 2 && /did not render result cards/.test(e.message)) {
await sleep(5000);
continue;
}
throw e;
}
} Prevention
- Run from a network path with reliable access to douyin.com.
- Complete any captcha/security interstitial in the bound browser before scripted runs.
- Keep the adapter's data-e2e selectors current after Douyin front-end deploys.
When it happens
Trigger: Slow network exceeding 15s; Douyin serving a CAPTCHA/verify interstitial that is neither login_wall nor results; SPA hydration failure; logged-out skeleton that doesn't classify as login_wall.
Common situations: Running from a region with slow/filtered access to douyin.com; Douyin A/B-testing new page markup that breaks the [data-e2e=scroll-list] hook; risk-control challenge shown to the session.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Malformed response from Douyin API (${method} ${url})
- Douyin transcode for video ${videoId}
- 抖音作品 ${awemeId} 删除后仍在作品列表中,删除未确认
- 等待抖音草稿编辑页超时
- 准备抖音自定义封面输入框失败: ${lastReason}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/f75de38be9a0da92.
Report an issue: GitHub.