unclecode/crawl4ai · error · RuntimeError
Wait condition failed: {str(e)}
Error message
Wait condition failed: {str(e)} What it means
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.
Source
Thrown at crawl4ai/async_crawler_strategy.py:994
# Handle user simulation — generate mouse movement and scroll
# signals that anti-bot systems look for, without firing keyboard
# events (ArrowDown triggers JS framework navigation) or clicking
# at fixed positions (may hit buttons/links and navigate away).
if config.simulate_user or config.magic:
await page.mouse.move(random.randint(100, 300), random.randint(150, 300))
await page.mouse.move(random.randint(300, 600), random.randint(200, 400))
await page.mouse.wheel(0, random.randint(200, 400))
# --- Phase 2: Wait for page readiness ---
if config.wait_for:
try:
timeout = config.wait_for_timeout if config.wait_for_timeout is not None else config.page_timeout
await self.smart_wait(
page, config.wait_for, timeout=timeout
)
except Exception as e:
raise RuntimeError(f"Wait condition failed: {str(e)}")
# Handle virtual scroll if configured (after wait_for so container exists)
if config.virtual_scroll_config:
await self._handle_virtual_scroll(page, config.virtual_scroll_config)
# Pre-content retrieval hooks and delay
await self.execute_hook("before_retrieve_html", page, context=context, config=config)
if config.delay_before_return_html:
await asyncio.sleep(config.delay_before_return_html)
# --- Phase 3: Post-wait JS (runs on fully-loaded page) ---
if config.js_code:
execution_result = await self.robust_execute_user_script(
page, config.js_code
)
if not execution_result["success"]:View on GitHub (pinned to 7e80152142)
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.
Example fix
// before
cfg = CrawlerRunConfig(wait_for="css:.search-results", wait_for_timeout=3000)
// after
cfg = CrawlerRunConfig(
wait_for="css:.search-results, .no-results",
wait_for_timeout=15000,
) Defensive patterns
Strategy: try-catch
Validate before calling
async def selector_present(browser_page, css: str, timeout_ms=2000) -> bool:
try:
await browser_page.wait_for_selector(css, timeout=timeout_ms, state="attached")
return True
except Exception:
return False Try / catch
try:
result = await crawler.arun(url, config=cfg)
except RuntimeError as e:
if "Wait condition failed" in str(e):
result = await crawler.arun(url, config=CrawlerRunConfig(wait_for=None)) # degrade gracefully Prevention
- Validate selectors in devtools against the live page
- Size wait_for_timeout to the slowest observed render
- Catch per-URL in batch crawls
When it happens
Trigger: 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).
Common situations: 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).
Related errors
- timeout must be positive
- Failed on navigating ACS-GOTO: {str(e)}
- Body element is hidden: {visibility_info}
- Request timed out: {str(e)}
- Request timed out: {str(e)}
AI-assisted analysis of unclecode/crawl4ai@7e80152142 (2026-08-14).
Data as JSON: /api/errors/61ee8d021d4b43d7.
Report an issue: GitHub.