Repository navigation
Description
browse version
0.0.16 (2026-11-08T19:49:45.461Z), binary install. The extension is also 0.0.16, protocol 3. browse update --check says 0.0.16 is the current release.
Browser
Google Chrome on Windows, through the extension. The relay runs on Linux and listens on 127.0.0.1:24988.
Agent
Claude Code 2.1.322 (MCP browse mcp), and the browse CLI for the reproduction.
Platform
Linux
System details
Ubuntu 26.04.1 LTS, kernel 7.0.0-43-generic.
What happened?
When the caller disconnects while an execute waits on page.goto, the relay reports the execute as stopped, but the script goes on. After that, every new execute fails, also return 1;:
Timeout 15000ms exceeded.
Call log:
- <ws preparing> retrieving websocket url from http://127.0.0.1:24988
- <ws connecting> ws://127.0.0.1:24988/devtools/browser/<id>
- <ws connected> ws://127.0.0.1:24988/devtools/browser/<id>
The WebSocket connects, but connectOverCDP never finishes. browse doctor reports every check as [ok] during the hang. The executes work again only after session_reset or session_delete closes the session's tab.
This is close to #109, but it is a different trigger. It is Chrome, not Vivaldi, the tab already exists, and the hang starts after a caller disconnect while a navigation is pending.
First time (MCP)
- An execute in session
mcp-ab67524bran 3 ×page.goto("https://istyle.bg/products/<handle>.json"), thenpage.goto("https://istyle.bg/pages/business")andwaitForTimeout(2000). - The MCP client cancelled the call. The journal entry is
isError: true, durationMs: 1573, "Execute stopped because its caller disconnected.", diagnostic: "client-disconnected". - The tab still went on to
https://istyle.bg/pages/business, which is the last navigation in the script.statusthen showed the session withconnected: false, plusactiveTargets: 1,childTargets: 7, andcdpClients: 0. - The next three executes each failed with the 15 s timeout above. One of them was
return 1;. session_resetclosed the tab. The nextreturn 1;returned in 160 ms.
Reproduction (CLI)
loop.js:
const hs = [
"14-inch-macbook-pro-m6-apple-m6-12-cpu-12-gpu-24gb-1tb-standard-display-finish-space-black-russian-1f11j1",
"14-inch-macbook-pro-m6-apple-m6-12-cpu-12-gpu-32gb-1tb-standard-display-finish-silver-bulgarian-c54yjl",
"14-inch-macbook-pro-m6-apple-m6-12-cpu-12-gpu-16gb-1tb-standard-display-finish-space-black-bulgarian-19lzpp",
];
for (const h of hs) {
await page.goto(`https://istyle.bg/products/${h}.json`, { waitUntil: "domcontentloaded" });
await page.evaluate(() => document.body.innerText);
}
await page.goto("https://istyle.bg/pages/business", { waitUntil: "domcontentloaded" });
await page.waitForTimeout(2000);
return page.url();Steps. First create the session repro1 with the MCP tool session_new, then run:
browse execute -s repro1 'await page.goto("https://istyle.bg/products/14-inch-macbook-pro-m6-mhwt4ze-a"); return page.url()'
timeout -s KILL 1.6 browse execute -s repro1 --file loop.js
timeout 40 browse execute -s repro1 --timeout-ms 30000 'return page.url()'The last command did not return in 40 s, although it had --timeout-ms 30000. I tried it three times, and I killed the client after 0.8 s, 1.6 s, and 2.5 s. It hung each time.
Other things that I saw in the reproduction:
- The relay did not see the killed CLI as a disconnected caller until the 60 s default timeout. The journal entry for
loop.jsisdurationMs: 60105, "Execute stopped because its caller disconnected.". - While
repro1was stuck, a freshbrowse execute --rm --timeout-ms 20000 'return 1'also failed withDiagnostic: execute-timeoutafter 20 s. So the block may affect other sessions too. Another agent started a session on the same relay at that time, so this one test is not clean. session_resetforrepro1returnedSession reset timed out waiting for active execute in repro1. Right after it, a fresh--rmreturn 1returned in 2.5 s.- The relay log has
process.benignRejections count=1 ... example="Protocol error (Network.setCacheDisabled): Internal server error, session closed."at the same times. - The tabs of the stopped executes stayed open, with
relay-owned=4inbrowse doctor, until I deleted the sessions.
Two cancels with no pending navigation did not block anything:
- An MCP cancel at 826 ms during
await page.waitForTimeout(20000). The nextreturn 1;worked. - A relay timeout (
timeoutMs: 1500) duringpage.goto. The next execute worked.
What I think happens
I did not confirm these causes in the code.
- A caller disconnect marks the execute as stopped, but nothing interrupts the running script. Its navigations still happen.
- The stopped execute still holds the session's execute lock, its CDP connection, or both. A new
connectOverCDPthen waits on that tab or its child targets after<ws connected>, until the tab closes. - The relay notices a killed CLI client only at the script timeout, not when the connection drops.
Expected
- A caller disconnect stops the script, including its pending
page.gotocalls. - A stopped execute releases its CDP connection and its lock, so the next execute connects at once.
--timeout-msalso limits the time that an execute waits to connect.browse doctorreports a session that holds a stopped execute.
Activity
thdxr commentedon Nov 12, 2026
when a script got stopped while a page was still loading (the caller went away, or it hit its timeout), the page kept loading in the background and the tab stopped answering. that's why every later execute in that session hung until the tab was closed.
--timeout-msnow also covers time spent waiting behind another call on the same session, so a call can't hang past itfixed in #239, it'll be in the next release
— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊