fix: cap DOM build and screenshot tasks per BrowserStateRequestEvent (#4579)#4862
Open
wtfashwin wants to merge 3 commits into
Open
fix: cap DOM build and screenshot tasks per BrowserStateRequestEvent (#4579)#4862wtfashwin wants to merge 3 commits into
wtfashwin wants to merge 3 commits into
Conversation
Closes browser-use#4579. on_BrowserStateRequestEvent awaited the DOM build and clean-screenshot tasks without a per-task cap. TimeoutWrappedCDPClient caps individual CDP calls, but a DOM build is many sequential CDP calls, so the aggregate could blow the 30s event budget when a remote browser goes silent mid-build (stale WebSocket — sends succeed, responses never arrive). Wrap each await with asyncio.wait_for using configurable caps (BROWSER_USE_DOM_BUILD_TIMEOUT_S=15s, BROWSER_USE_SCREENSHOT_TIMEOUT_S=8s), with the same defensive env-parsing guard as BROWSER_USE_CDP_TIMEOUT_S so nan/inf/non-positive values fall back instead of breaking every state build. On timeout the handler returns a minimal state for the slow component, freeing the event-bus slot fast.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #4579.
DOMWatchdog.on_BrowserStateRequestEventawaited the DOM build and clean-screenshot tasks without a per-task cap.TimeoutWrappedCDPClientalready caps individual CDP calls (BROWSER_USE_CDP_TIMEOUT_S, default 60s), but a single DOM build fans out into many sequential CDP calls — so the aggregate await can still blow the 30sBrowserStateRequestEventbudget when a remote browser goes silent mid-build (stale WebSocket: sends succeed, responses never arrive, no close frame).This change wraps each of the two awaits with
asyncio.wait_forusing configurable caps:BROWSER_USE_DOM_BUILD_TIMEOUT_S(default 15s)BROWSER_USE_SCREENSHOT_TIMEOUT_S(default 8s)On timeout, the handler returns a minimal state for the slow component (empty
selector_map/ null screenshot) instead of holding the event-bus slot for the full budget. The healthy path is unchanged.Env parsing mirrors the defensive guard used for
BROWSER_USE_CDP_TIMEOUT_Sinbrowser_use/browser/_cdp_timeout.py: only finite positive values take effect;nan/inf/ non-positive / unparseable values fall back to the hardcoded default with a warning. Without this guard a typo would silently make every state build either time out immediately (nan/0) or never (inf/negative).Why per-task and not per-CDP-call
The existing per-CDP-call timeout is needed but not sufficient:
Both must hold for the handler to fail fast end to end. The defaults stay well below the 30s
BrowserStateRequestEventbudget so the per-task cap actually fires before the event-level kill — otherwise it would add nothing over what the event bus already enforces.Out of scope
The issue thread also describes a bubus global event-bus lock that serializes unrelated browser sessions during recovery. That's a separate lock-scope problem in
bubusitself and is not addressed here.Test plan
New file:
tests/ci/browser/test_dom_watchdog_timeouts.py._parse_env_task_timeoutunit tests — unset/empty falls back, valid value used,nan/inf/0/-5/abcrejected, small positives (0.001) accepted.test_default_timeouts_are_sensible— invariants:0 < SCREENSHOT < DOM_BUILD < 30s.test_dom_build_timeout_returns_minimal_state— realBrowserSession+pytest-httpserver; monkeypatch_build_dom_tree_without_highlightsto await anasyncio.Eventthat is never set (the exact shape of a stale-WS hang). Asserts the handler returnsselector_map == {}, the screenshot survives independently, and total time < 10s (not the 30s the unwrapped await would have caused).test_screenshot_timeout_returns_state_without_screenshot— symmetric for the screenshot path; assertsscreenshot is None, the DOM survives with a real non-emptyselector_map, and elapsed < 10s.test_healthy_path_preserves_results— sanity: with default caps the wrap is transparent (real DOM + real screenshot returned).uv run pytest -vxs tests/ci/browser/test_dom_watchdog_timeouts.py— 9/9 passing.uv run pytest -vx tests/ci/browser/— 60 passing, 3 skipped (no regressions).uv run pyright— 0 errors on changed files; full repo 0 errors.uv run ruff check --fix && uv run ruff format— clean.Tests follow the project convention (
CLAUDE.md): no mocking — realBrowserSession, real event bus, real Chromium viapytest-httpserver. Only the leaf coroutine that normally fans into CDP is swapped viamonkeypatch.setattrfor one that awaits a releasedasyncio.Event, which is the precise shape of the failure mode described in the issue.Summary by cubic
Caps the DOM build and clean screenshot tasks in
DOMWatchdog.on_BrowserStateRequestEventwith per-task timeouts so a silent browser can’t block the 30s event budget. Fixes #4579 by failing fast and returning a minimal state instead of hanging.asyncio.wait_for:BROWSER_USE_DOM_BUILD_TIMEOUT_S(15s) andBROWSER_USE_SCREENSHOT_TIMEOUT_S(8s).selector_mapfor DOM orNonefor screenshot; frees the event-bus slot quickly.Written for commit 49b041b. Summary will update on new commits. Review in cubic