{"record":{"id":"f75de38be9a0da92","repo":"jackwener/OpenCLI","slug":"douyin-search-did-not-render-result-cards-within-t","errorCode":null,"errorMessage":"Douyin search did not render result cards within the timeout. Open the same search in Chrome and verify login/security state before retrying.","messagePattern":"Douyin search did not render result cards within the timeout\\. Open the same search in Chrome and verify login/security state before retrying\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/douyin/search.js","lineNumber":291,"sourceCode":"        try {\n            result = unwrapEvaluateResult(await page.evaluate(WAIT_AND_EXTRACT_JS(RENDER_TIMEOUT_MS)));\n        } catch (error) {\n            throw new CommandExecutionError(`Douyin search extraction failed: ${error instanceof Error ? error.message : String(error)}`);\n        }\n        if (!result || typeof result !== 'object') {\n            throw new CommandExecutionError('Douyin search: unexpected evaluator payload shape');\n        }\n        if (result.state === 'login_wall') {\n            throw new AuthRequiredError(\n                'www.douyin.com',\n                'Douyin search results are blocked behind a login wall — log in at https://www.douyin.com in Chrome first.',\n            );\n        }\n        if (result.state === 'empty') {\n            throw new EmptyResultError('douyin search', `No Douyin videos matched \"${keyword}\".`);\n        }\n        if (result.state === 'timeout') {\n            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.');\n        }\n        if (!Array.isArray(result.cards)) {\n            throw new CommandExecutionError('Douyin search: evaluator returned malformed cards payload');\n        }\n        if (result.cards.length === 0) {\n            throw new EmptyResultError('douyin search', `No Douyin videos matched \"${keyword}\".`);\n        }\n        const projected = projectSearchCards(result.cards, limit);\n        if (projected.invalidCount > 0) {\n            throw new CommandExecutionError('Douyin search parser found result cards without stable video url or description');\n        }\n        if (projected.rows.length === 0) {\n            throw new EmptyResultError('douyin search', `No Douyin videos matched \"${keyword}\".`);\n        }\n        return projected.rows;\n    },\n});\n","sourceCodeStart":273,"sourceCodeEnd":309,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/douyin/search.js#L273-L309","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// operator workaround: retry with backoff\n// before\nconst rows = await douyinSearch(keyword);\n// after\nimport { CommandExecutionError } from '@jackwener/opencli/errors';\nlet rows;\nfor (let i = 0; i < 2 && !rows; i++) {\n  try { rows = await douyinSearch(keyword); }\n  catch (e) {\n    if (!(e instanceof CommandExecutionError) || !/timeout/.test(e.message)) throw e;\n    await new Promise(r => setTimeout(r, 5000));\n  }\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for (let attempt = 1; attempt <= 2; attempt++) {\n  try { return await douyinSearch(keyword); }\n  catch (e) {\n    if (attempt < 2 && /did not render result cards/.test(e.message)) {\n      await sleep(5000);\n      continue;\n    }\n    throw e;\n  }\n}","preventionTips":["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."],"tags":["timeout","rendering","douyin","anti-bot"],"backgroundTag":"page-render-timeout","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}