You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Bug: Agent.run raises CancelledError from a bubus parent timeout in a task nobody cancelled #5977
0.13.1 where I hit it; the code paths are the same in 0.13.10 and main at 302d8fc, which pin bubus==1.5.6
Bug Description, Steps to Reproduce, Screenshots
Agent.run() can end with asyncio.CancelledError: Event handler browser_use.browser.watchdog_base.DOMWatchdog.on_BrowserStateRequestEvent#...(...) was interrupted because of a parent timeout, raised in a task that nobody cancelled (asyncio.current_task().cancelling() == 0). CancelledError is a BaseException, so Agent.step's except Exception (agent/service.py#L1080-L1082) and the caller's except Exception both miss it. I hit this in a production app. It comes from get_browser_state_summary (via step -> _prepare_context), where event.event_result(raise_if_none=True, raise_if_any=True) re-raises the stored CancelledError of DOMWatchdog.on_BrowserStateRequestEvent after it ran inline inside BrowserSession.on_NavigateToUrlEvent.
The cause is in bubus 1.5.6 (main has the same code):
A handler that awaits a child event processes the queued events of every EventBus in the process inside its own task (models.py#L303-L313); a BrowserStateRequestEvent queued by an agent can run inline inside a navigate handler.
When that navigate handler hits its timeout, the inline handler is cancelled with it, and bubus stores a new CancelledError("... interrupted because of a parent timeout") as that event's result (service.py#L1162-L1174).
If the interrupted handler had dispatched a child (DOMWatchdog dispatches a ScreenshotEvent), the child's completion later marks the event complete, and event_result() re-raises the stored CancelledError to the awaiter (models.py#L507-L516); the loop meant to ignore errors while collecting results only catches Exception (models.py#L487-L492). Without a child, the event never completes and the awaiter gets a bare TimeoutError after event_timeout.
get_browser_state_summary handles TimeoutError since 0.13.10 (session.py#L1633-L1635), but not this CancelledError.
Draining every bus is also the root cause of #5509. browser-use/bubus#34 drains only the awaited chain and fixes the repro below; browser-use/bubus#30 does not. The repro uses bubus alone (one loop, one bus, no browser) with browser-use's event names.
Failing Python Code
importasynciofromimportlib.metadataimportversionfrombubusimportBaseEvent, EventBusclassNavigateToUrlEvent(BaseEvent[None]):
event_timeout: float|None=0.5classNavigationCompleteEvent(BaseEvent[None]):
passclassBrowserStateRequestEvent(BaseEvent[str]):
event_timeout: float|None=5.0classScreenshotEvent(BaseEvent[str]):
passasyncdefmain() ->None:
print('bubus', version('bubus'))
bus=EventBus(name='Session')
asyncdefon_navigate(event: NavigateToUrlEvent) ->None:
# awaiting a child inside a handler makes bubus run other queued events inline, in this taskawaitbus.dispatch(NavigationCompleteEvent())
asyncdefon_navigation_complete(event: NavigationCompleteEvent) ->None:
passasyncdefon_state_request(event: BrowserStateRequestEvent) ->str:
bus.dispatch(ScreenshotEvent()) # like DOMWatchdog, which dispatches a ScreenshotEventawaitasyncio.sleep(2) # longer than the navigate handler's 0.5 s timeoutreturn'state'asyncdefon_screenshot(event: ScreenshotEvent) ->str:
return'png'bus.on(NavigateToUrlEvent, on_navigate)
bus.on(NavigationCompleteEvent, on_navigation_complete)
bus.on(BrowserStateRequestEvent, on_state_request)
bus.on(ScreenshotEvent, on_screenshot)
bus.dispatch(NavigateToUrlEvent())
state_request=bus.dispatch(BrowserStateRequestEvent())
try:
print('result:', awaitstate_request.event_result(raise_if_any=True, raise_if_none=True))
exceptExceptionase:
print('caught by except Exception:', type(e).__name__, e)
exceptasyncio.CancelledErrorase:
print(f'CancelledError in a task with cancelling()={asyncio.current_task().cancelling()}: {e}')
awaitbus.stop(clear=True)
asyncio.run(main())
LLM Model
None (no agent or LLM involved in the repro)
Operating System & Browser Versions
macOS, Python 3.11; the repro launches no browser
Full DEBUG Log Output
bubus 1.5.6
CancelledError in a task with cancelling()=0: Event handler __main__.on_state_request#9888(?▶ BrowserStateRequestEvent#89dc 🏃) was interrupted because of a parent timeout
Expected: Agent.run() does not raise CancelledError unless its own task was cancelled; a state request interrupted by another handler's timeout surfaces as an ordinary step error.
Possible fixes: merge and release browser-use/bubus#34 and bump the pin. Until then, get_browser_state_summary could handle a CancelledError caught while asyncio.current_task().cancelling() == 0 like its TimeoutError. In bubus, storing a non-cancellation error for the interrupted handler (the TODO at service.py#L1167 mentions InterruptedError) would also stop the re-raise.
Activity