unclecode/crawl4ai · error · Error
Body element is hidden: {visibility_info}
Error message
Body element is hidden: {visibility_info} What it means
Raised when the crawled page's <body> element is detected as hidden (display:none, visibility:hidden, opacity:0, or zero size) after the Playwright wait_for_selector on body times out or errors. The visibility details from check_visibility(page) are embedded in the message. It is skipped entirely when config.ignore_body_visibility is true.
Source
Thrown at crawl4ai/async_crawler_strategy.py:844
timeout=30000,
)
if not is_visible and not config.ignore_body_visibility:
visibility_info = await self.check_visibility(page)
raise Error(f"Body element is hidden: {visibility_info}")
except Error:
visibility_info = await self.check_visibility(page)
if self.browser_config.verbose:
self.logger.debug(
message="Body visibility info: {info}",
tag="DEBUG",
params={"info": visibility_info},
)
if not config.ignore_body_visibility:
raise Error(f"Body element is hidden: {visibility_info}")
# try:
# await page.wait_for_selector("body", state="attached", timeout=30000)
# await page.wait_for_function(
# """
# () => {
# const body = document.body;
# const style = window.getComputedStyle(body);
# return style.display !== 'none' &&
# style.visibility !== 'hidden' &&
# style.opacity !== '0';
# }
# """,
# timeout=30000,
# )
# except Error as e:
# visibility_info = await page.evaluate(View on GitHub (pinned to 7e80152142)
Solutions
- Set CrawlerRunConfig(ignore_body_visibility=True) when the target is known to hide body initially.
- Add wait_for / js_code to wait until the app finishes rendering (e.g. wait for a specific selector) before visibility is checked.
- Increase page_timeout so hydration completes; use wait_until='networkidle' or 'domcontentloaded' as appropriate.
- If the site blocks headless browsers, adjust the user agent or use an appropriate adapter.
Example fix
// before
cfg = CrawlerRunConfig()
result = await crawler.arun(url=spa_url, config=cfg)
// after
cfg = CrawlerRunConfig(
ignore_body_visibility=True,
wait_for="css:.app-root[data-ready='true']",
)
result = await crawler.arun(url=spa_url, config=cfg) Defensive patterns
Strategy: fallback
Try / catch
try:
result = await crawler.arun(url, config=cfg)
except Exception as e:
if "Body element is hidden" in str(e):
cfg2 = CrawlerRunConfig(**{**cfg.__dict__, "ignore_body_visibility": True})
result = await crawler.arun(url, config=cfg2) Prevention
- Set ignore_body_visibility=True for known SPAs
- Use wait_for to wait for real content selectors
- Prefer wait_until='networkidle' for hydration-heavy apps
When it happens
Trigger: Pages that keep body hidden until JS runs (loading screens, cookie walls, SPA shells, geo-block pages, pages that render only after user interaction). Also pages where an overlay CSS hides body, or where content is inside frames/containers while body itself never becomes visible.
Common situations: SPAs (React/Vue/Angular) with slow hydration hiding body behind a loader; sites that block bots by leaving body hidden; pages requiring cookies/login before revealing content; headless-mode-specific CSS (some sites hide body for headless user agents).
Related errors
- Failed on navigating ACS-GOTO: {str(e)}
- Wait condition failed: {str(e)}
- Failed to extract HTML content: {str(e)}
- Failed to start browser: {e}
- Unsupported browser type: {browser_type}
AI-assisted analysis of unclecode/crawl4ai@7e80152142 (2026-08-14).
Data as JSON: /api/errors/1cdb63b7eb2976f7.
Report an issue: GitHub.