Skip to content

fix(crawler): warn when the body-visibility wait times out - #2145

Merged
ntohidi merged 2 commits into
developfrom
fix/issue-2144
Aug 17, 2026
Merged

fix(crawler): warn when the body-visibility wait times out#2145
ntohidi merged 2 commits into
developfrom
fix/issue-2144

Conversation

@ntohidi

@ntohidi ntohidi commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #2144.

When the body-visibility wait times out, nothing is logged. The crawl reports success=True with an empty error_message, and body_visibility_timeout ms disappear with no indication of where they went — because the wait's result is discarded whenever ignore_body_visibility is True (the default).

That silence is what made the delay in #2129 impossible to attribute without instrumenting the pipeline, and what makes body_visibility_timeout (added in #2131) undiscoverable by exactly the users who need it.

This warns once when the wait times out and its result is ignored, naming the option and the timeout that actually applied. The strict path (ignore_body_visibility=False) already raises with visibility details, so it stays quiet — no double reporting.

No config, no behavior change, no new dependency. One log line.

List of files changed and why

  • crawl4ai/async_crawler_strategy.py — warn after the visibility wait when it timed out and the result is about to be discarded.
  • tests/test_body_visibility_warning.py — new: warns and reports the applied timeout; silent when the body is visible; silent on the strict path.

How Has This Been Tested?

Live output on the #2129 repro (two identical pages differing only by ng-cloak on <body>) — warning on the hidden page, silence on the visible one:

[COMPLETE] ● http://127.0.0.1:8897/visible  | ✓ | ⏱: 0.34s
[WARNING]. ⚠ Body never became visible after 30000ms — the page may use ng-cloak/v-cloak.
             This delay is added to every crawl of this page; lower
             CrawlerRunConfig.body_visibility_timeout to shorten it.
[COMPLETE] ● http://127.0.0.1:8897/hidden   | ✓ | ⏱: 30.25s
  • tests/test_body_visibility_warning.py + tests/test_config_defaults.py — 43 passed.
  • Fail-on-revert verified: with the change reverted, test_warns_when_wait_times_out_and_result_is_discarded fails.
  • tests/regression/ — 320 passed, 2 failed. Both fail identically on develop without this change: test_cosine_basic is an ImportError from the missing optional transformers extra, and test_soft_404_filters_probes is pre-existing.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation — N/A; no config or behavior change, only a log line.
  • I have added/updated unit tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

The wait result is discarded when ignore_body_visibility is True (the
default), so a page whose body never becomes visible — ng-cloak/v-cloak
left behind by an app that failed to bootstrap — costs a flat
body_visibility_timeout ms on every crawl while still reporting
success=True with an empty error_message.

Nothing was logged, so the delay could only be found by instrumenting the
pipeline, and body_visibility_timeout (added in #2131) was undiscoverable
by the users who most needed it. Warn once when the wait times out and
its result is ignored, naming the option and the timeout that applied.

The strict path (ignore_body_visibility=False) already raises with
visibility details, so it stays quiet.

Fixes #2144
Review follow-up on three points:

force_verbose — AsyncLogger drops a plain warning when verbose is off
(async_logger.py:238), and CrawlerRunConfig.verbose overrides the logger
at arun() time. Servers and batch jobs run with verbose off, so the
warning was suppressed in exactly the deployments that hit this and can
least afford a silent 30s. Verified end to end: with verbose=False the
warning was invisible before, and prints now.

False positives — csp_compliant_wait also returns False when the page
evaluation itself fails (page closed, context destroyed by a redirect),
which costs no time. Time the wait and only warn when it actually burned
its budget, and report the elapsed time rather than the configured
timeout so the number matches the delay being explained.

Test — select the warning call by message instead of reading the last
call, which only passed because text_mode=True happened to skip the
wait_for_images warning. Added coverage for force_verbose and for the
early-failure path.
@ntohidi

ntohidi commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed all three review findings in 1bac351.

2 — invisible where it matters most. Confirmed and worse than described: AsyncLogger._log gates on self.verbose or force_verbose (async_logger.py:238), and arun() overwrites the logger's verbose from CrawlerRunConfig.verbose at async_webcrawler.py:257. So a run config with verbose=False suppressed the warning regardless of how the crawler was built. Verified end to end on the #2129 repro — before this commit the hidden-body page produced 30s of total silence with verbose=False; now it warns. Added force_verbose=True and a test asserting it.

1 — warning can be false. Fixed by timing the wait and only warning when it actually consumed its budget (>= 90% of body_visibility_timeout). An eval failure returns False immediately, so it no longer gets blamed on the timeout. The message now reports elapsed time rather than the configured value, so the number matches the delay the user is trying to explain (after 30093ms). Added a test for the early-failure path.

3 — brittle assertion. Now selects the warning call by message instead of reading the last call.

Tests: 45 passed (5 in this file, up from 3, plus test_config_defaults.py). tests/regression/ — 320 passed, 2 failed, both failing identically on develop without this branch (test_cosine_basic is a missing optional transformers extra; test_soft_404_filters_probes is pre-existing).

@ntohidi
ntohidi merged commit d7af46e into develop Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant