{"record":{"id":"61ee8d021d4b43d7","repo":"unclecode/crawl4ai","slug":"wait-condition-failed-str-e","errorCode":null,"errorMessage":"Wait condition failed: {str(e)}","messagePattern":"Wait condition failed: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"crawl4ai/async_crawler_strategy.py","lineNumber":994,"sourceCode":"            # Handle user simulation — generate mouse movement and scroll\n            # signals that anti-bot systems look for, without firing keyboard\n            # events (ArrowDown triggers JS framework navigation) or clicking\n            # at fixed positions (may hit buttons/links and navigate away).\n            if config.simulate_user or config.magic:\n                await page.mouse.move(random.randint(100, 300), random.randint(150, 300))\n                await page.mouse.move(random.randint(300, 600), random.randint(200, 400))\n                await page.mouse.wheel(0, random.randint(200, 400))\n\n            # --- Phase 2: Wait for page readiness ---\n\n            if config.wait_for:\n                try:\n                    timeout = config.wait_for_timeout if config.wait_for_timeout is not None else config.page_timeout\n                    await self.smart_wait(\n                        page, config.wait_for, timeout=timeout\n                    )\n                except Exception as e:\n                    raise RuntimeError(f\"Wait condition failed: {str(e)}\")\n\n            # Handle virtual scroll if configured (after wait_for so container exists)\n            if config.virtual_scroll_config:\n                await self._handle_virtual_scroll(page, config.virtual_scroll_config)\n\n            # Pre-content retrieval hooks and delay\n            await self.execute_hook(\"before_retrieve_html\", page, context=context, config=config)\n            if config.delay_before_return_html:\n                await asyncio.sleep(config.delay_before_return_html)\n\n            # --- Phase 3: Post-wait JS (runs on fully-loaded page) ---\n\n            if config.js_code:\n                execution_result = await self.robust_execute_user_script(\n                    page, config.js_code\n                )\n\n                if not execution_result[\"success\"]:","sourceCodeStart":976,"sourceCodeEnd":1012,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/crawl4ai/async_crawler_strategy.py#L976-L1012","documentation":"Raised when the user-supplied wait_for condition (a CSS selector, JS function string, or js_condition) fails or times out inside smart_wait(). The timeout used is config.wait_for_timeout if set, otherwise config.page_timeout; the underlying exception text is wrapped in a RuntimeError with the 'Wait condition failed' prefix.","triggerScenarios":"Setting CrawlerRunConfig(wait_for='css:.results', wait_for_timeout=5000) on a page where .results never appears within the timeout; a wait_for js: expression that throws or returns falsy forever; a selector that appears only after interaction (click, scroll, login).","commonSituations":"Selector names change after a site redesign; A/B-tested pages where the selector sometimes never renders; timeout too short for slow SPAs or rate-limited APIs backing the page; wait_for expression syntax errors (invalid JS).","solutions":["Verify the selector/JS condition manually in browser devtools against the exact crawled URL.","Increase wait_for_timeout (or page_timeout) to cover slow rendering.","Make the condition resilient: wait for a selector that always exists once content is meaningful, or use 'js:' conditions that poll document readiness.","Catch the RuntimeError per-URL when crawling many pages so one bad page does not abort the batch."],"exampleFix":"// before\ncfg = CrawlerRunConfig(wait_for=\"css:.search-results\", wait_for_timeout=3000)\n\n// after\ncfg = CrawlerRunConfig(\n    wait_for=\"css:.search-results, .no-results\",\n    wait_for_timeout=15000,\n)","handlingStrategy":"try-catch","validationCode":"async def selector_present(browser_page, css: str, timeout_ms=2000) -> bool:\n    try:\n        await browser_page.wait_for_selector(css, timeout=timeout_ms, state=\"attached\")\n        return True\n    except Exception:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    result = await crawler.arun(url, config=cfg)\nexcept RuntimeError as e:\n    if \"Wait condition failed\" in str(e):\n        result = await crawler.arun(url, config=CrawlerRunConfig(wait_for=None))  # degrade gracefully","preventionTips":["Validate selectors in devtools against the live page","Size wait_for_timeout to the slowest observed render","Catch per-URL in batch crawls"],"tags":["wait-for","selector","timeout","spa"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}