Skip to content

A caller disconnect during page.goto leaves the execute running and blocks later executes (connectOverCDP times out after <ws connected>) #238

Closed

Description

@VanTanev

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)

  1. An execute in session mcp-ab67524b ran 3 × page.goto("https://istyle.bg/products/<handle>.json"), then page.goto("https://istyle.bg/pages/business") and waitForTimeout(2000).
  2. The MCP client cancelled the call. The journal entry is isError: true, durationMs: 1573, "Execute stopped because its caller disconnected.", diagnostic: "client-disconnected".
  3. The tab still went on to https://istyle.bg/pages/business, which is the last navigation in the script. status then showed the session with connected: false, plus activeTargets: 1, childTargets: 7, and cdpClients: 0.
  4. The next three executes each failed with the 15 s timeout above. One of them was return 1;.
  5. session_reset closed the tab. The next return 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.js is durationMs: 60105, "Execute stopped because its caller disconnected.".
  • While repro1 was stuck, a fresh browse execute --rm --timeout-ms 20000 'return 1' also failed with Diagnostic: execute-timeout after 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_reset for repro1 returned Session reset timed out waiting for active execute in repro1. Right after it, a fresh --rm return 1 returned 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=4 in browse 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 next return 1; worked.
  • A relay timeout (timeoutMs: 1500) during page.goto. The next execute worked.

What I think happens

I did not confirm these causes in the code.

  1. A caller disconnect marks the execute as stopped, but nothing interrupts the running script. Its navigations still happen.
  2. The stopped execute still holds the session's execute lock, its CDP connection, or both. A new connectOverCDP then waits on that tab or its child targets after <ws connected>, until the tab closes.
  3. 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.goto calls.
  • A stopped execute releases its CDP connection and its lock, so the next execute connects at once.
  • --timeout-ms also limits the time that an execute waits to connect.
  • browse doctor reports a session that holds a stopped execute.

Activity

  1. thdxr commented on Nov 12, 2026

    @thdxr
    Member

    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.

    • stopping a script now also stops the page load, so the next execute connects right away
    • --timeout-ms now also covers time spent waiting behind another call on the same session, so a call can't hang past it

    fixed in #239, it'll be in the next release

    — from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions