Fix alert dialog detection and handling capabilities?
Description of the bug
There is an issue that was auto-closed for this: #529 However, even today, with running this MCP through Claude Code, I'm having trouble with this functionality. My use case is simple - when an alert dialog appears on page, the process halts and times outs. The actual handle_dialog() call gets executed only AFTER I manually click OK in the browser (when running interactively - in headless mode, I just have to wait for the timeout). The interesting thing is that this call even thinks it handled the OK button itself - it does not report an error (which is otherwise does, when no dialog is on page because I already clicked OK before handle_dialog could execute but after the MCP found out about a dialog being present on page).
Not sure if this feature is broken only for a handful of us or it's something else but I can confirm that the issue is still there.
Reproduction
Please see #529
Expectation
Please see #529
MCP configuration
{ "mcpServers": { "chrome-devtools": { "command": "npx", "args": [ "chrome-devtools-mcp@latest", "--channel=stable", "--isolated=true", "--viewport=1920x1080" ] } } }
Chrome DevTools MCP version
current, latest
Chrome version
143.0.7499.169
Coding agent version
2.0.76
Model version
opus, sonnet, haiku - all of them
Chat log
what you'd expect - handle_dialog only appears after I press OK, everything else works smoothly afterwards (the call even thinks it handled the OK button itself)
Node version
24.12.0
Operating system
Windows Subsystem for Linux (WSL)
Extra checklist
- [ ] I want to provide a PR to fix this bug
Thanks for reporting, do you have steps to reproduce? Which process is timing out for you? is the dialog caused by a script evaluation tool call or by input?
Just as you commented, I'm running a set of fixes and while trying a fix via MCP, Claude Code has this very issue. Here is where I'm currently stuck:
Claude is instructed to test its fixes in browser. It does this but as it tries to navigate away, the onbeforeunload event hits and displays a leaving message:
And the browser, alongside with Claude get stuck on this page, waiting until a timeout is reached. If I click the Leave button itself, the workflow continues but that basically defeats the purpose and also shows wrong continuation in console (it would appear that Claude has accepted the dialog - but that info is wrong, as the actual navigation timed out while I was writing this comment :-D and then I clicked the Leave button manually):
So this is the exact workflow that fails me and was failing me for a long time now.
Thanks, it sounds like it might be specific to before unload dialogs. So when the handle_dialog runs, the dialog on screen does not close? or does it close but gets triggered again by the next navigate call? Seeing https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/tools/pages.ts#L256 would it be possible for you to record a log file and see if there are errors when handling the dialog? https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/troubleshooting.md#debugging
Yes, it seems to be specific to onBeforeUnload events. When the handle_dialog() runs, the previous wait for a dialog already timed out (with the dialog still present on page - thus the next retry which followed on the last screenshot above, which also timed out). The handle_dialog() function actually lies, as it never accomplishes anything in this case. When I manually click "Reload" (or "Leave") button, the handle_dialog() function is only presented AFTERWARDS and again lies about it doing anything. So the function actually never fires in time, waiting in a background loop of the onBeforeUnload dialog, never catching it.
I did as instructed and here is the new Claude Code log + I'm attaching the debug log as well (I manually clicked the Reload button this time, I did not wait for a timeout):
There is also another issue which is not handled by handle_dialog() and this one might be even worse for CI, since it blocks Chrome until an OK button is clicked. When a compromised password was found to be entered in a password field, Chrome displays a warning dialog about this after the login is complete:
In this case, the browser is blocked from any keyboard interaction apart from JavaScript, so if I try to change something on page, I can't do it until this is manually clicked on, dev tools devising JS strategies to overcome this, breaking any e2e tests that depend on manipulating an input:
I'm attaching a debug log (from a later run without the CTRL+A step, as I did not record this first try) for this as well.
chromedev-compromises-password.log chromedev-onbeforeunload.log
The change password dialog should be non-blocking but there is unfortunately no way currently to handle it via CDP (it should be possible to disable that in chrome://settings though).
I see that there was an attempt to handle the dialog but it was not showing (I guess because you clicked it manually?):
2026-01-07T13:56:32.667Z puppeteer:protocol:SEND ► [
'{"method":"Page.handleJavaScriptDialog","params":{"accept":false},"id":180,"sessionId":"D47DD0BA5BFEFBEE3FF7EBDB67426204"}'
]
2026-01-07T13:56:32.668Z puppeteer:protocol:RECV ◀ [
'{"id":180,"error":{"code":-32602,"message":"No dialog is showing"},"sessionId":"D47DD0BA5BFEFBEE3FF7EBDB67426204"}'
]
cc @natorion we need to look into the beforeunload dialogs as they seem to block the navigations. cc @sebastianbenz do you know if there is a way to disable password dialogs from the browser?
I see that there was an attempt to handle the dialog but it was not showing (I guess because you clicked it manually?):
2026-01-07T13:56:32.667Z puppeteer:protocol:SEND ► [ '{"method":"Page.handleJavaScriptDialog","params":{"accept":false},"id":180,"sessionId":"D47DD0BA5BFEFBEE3FF7EBDB67426204"}' ] 2026-01-07T13:56:32.668Z puppeteer:protocol:RECV ◀ [ '{"id":180,"error":{"code":-32602,"message":"No dialog is showing"},"sessionId":"D47DD0BA5BFEFBEE3FF7EBDB67426204"}' ]
Yes, this is what happens after you click it manually - the MCP still thinks the dialog is there but it was there for about 30 seconds, then I clicked it and only then the MCP workflow resumed and handle_dialog() realized (late) there is a dialog (which was already gone after I clicked it).
On beforeunload dialogs: What's the behavior in puppeteer for this exact same use case?
On beforeunload dialogs: What's the behavior in puppeteer for this exact same use case?
it would trigger the dialog and the navigation would time out, same as with the MCP server. In contrast to the MCP server, Puppeteer can handle the event about the dialog during the navigation preventing the time out.