Repository navigation
Fix browser tools hanging for minutes on dialogs and stuck pages - #216
Merged
Merged
Conversation
Chrome stalls the renderer while an alert/confirm/prompt/beforeunload dialog is open, and chromiumoxide leaves it open. The click that raised it did not return, and every later CDP command (evaluate, screenshot) waited for its timeout: one browser_act took many minutes and ended in "Request timed out", and the dialog stayed open for the next call. - BrowserSession answers dialogs as they open: alert and beforeunload are accepted, confirm and prompt dismissed unless accepting was requested. Answered dialogs are reported in the next observation. - browser_act takes accept_dialogs for the call; the result notes each dialog and whether it was accepted or dismissed.
chromiumoxide's key table only knows the US layout, so type and fill failed with "Key not found" on the first umlaut, ß or €. Characters on the layout are still pressed as real keys; all others are inserted with Input.insertText into the focused element.
- navigate() followed goto with wait_for_navigation, which has no timeout in chromiumoxide. goto already waits for the load event; the second wait only added a hang when a script redirect started right after loading. - LaunchedBrowser::close waited for the process to exit without a limit. After 10 seconds the browser is now killed instead.
chromiumoxide's own 30s command timeout does not hold when the renderer is stuck: a click on a hung page blocked for minutes, a screenshot for about 16 minutes. One browser tool call could stall the agent that long. - BrowserSession bounds every verb: 15s for an interaction, 30s for a navigation (BrowserTimeouts, adjustable via with_timeouts). A limit that runs out is a typed BrowserTimeout. wait_for and settle are bounded as a whole, so a poll that never returns cannot outlast them. - capture stops at the first unanswered read and reports that the page is not responding, instead of waiting out the screenshot as well. - A navigation whose load event does not fire in time (a slow iframe or tracker) is no longer a failure: the page is shown as far as it got, with a note. The login handoff tolerates it the same way, so a slow portal page cannot fail a login the user already completed.
async-trait up to 0.1.89 added a message-less #[must_use] to every async trait method, whose boxed future is already must_use. Clippy 1.99 flags that as double_must_use; 0.1.92 no longer emits the attribute.
stippi
force-pushed
the
fix/browser-dialogs
branch
from
October 2, 2026 08:55
14bb9e6 to
b0a959e
Compare
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.
Problem
The browser tools sometimes hung for minutes and then failed with "Request timed out". The engine is
chromiumoxide(CDP), which lacks protections Playwright provides out of the box. A local test reproduced it:alert()did not return within 120 s.chromiumoxide's own 30 s command timeout does not hold while the renderer is stuck.
Changes
alertandbeforeunloadare accepted.confirmandpromptare dismissed unlessbrowser_actgetsaccept_dialogs: truefor that call. Each answered dialog is reported to the model (Note: a confirm dialog "…" was dismissed.).BrowserSessionverb is bounded: 15 s for an interaction, 30 s for a navigation (BrowserTimeouts). A limit that runs out is a typedBrowserTimeout.wait_forandsettleare bounded as a whole.loadevent never fires (a slow iframe or tracker) is no longer a failure. The page is shown as far as it got, with a note. The login handoff tolerates this too, so a slow portal page cannot fail a login the user already completed.ü,ß,€) used to fail with "Key not found". They are now inserted withInput.insertText.navigate()dropped its secondwait_for_navigation, which has no timeout in chromiumoxide.LaunchedBrowser::closekills the browser if it does not exit within 10 s.Notes for reviewers
confirmon purpose. Accepting could trigger an outward action, such as submitting a form, whose warning the model never saw.WebClient::fetch(theweb_fetchtool) has the same unboundedwait_for_navigationafternew_page. It is not part of this PR.test_web_searchneeds network access to DuckDuckGo. It failed locally because the network was unavailable, not because of this change.Testing
crates/web/src/tests.rs:crates/code_assistant_core/src/tools/impls/browser.rs:browser_actcargo test --package webandcargo test --package code_assistant_corepass.cargo clippy --all-targets --all-features -- -D warningsandcargo fmt --checkare clean.