Skip to content

Chrome MCP pending attach abort can cancel unrelated waiters #88304

@rohitjavvadi

Description

@rohitjavvadi

Problem

Chrome MCP cached session creation shares one pending attach promise per profile/cache key, but that shared attach currently uses the first caller's AbortSignal. If that first caller cancels while another caller is waiting for the same attach, the shared pending promise can reject with the cancelled caller's reason and the unrelated caller fails too.

That can make concurrent browser operations flaky around timeouts/cancellations: one abandoned request can cancel another request that still wants the same Chrome MCP session.

Source evidence

On current origin/main (b0d6076208193231da906244abf2fdc1d88a2ccf):

  • extensions/browser/src/browser/chrome-mcp.ts:843 starts the shared cached-session path when no cached session exists.
  • extensions/browser/src/browser/chrome-mcp.ts:846 creates a shared pending promise.
  • extensions/browser/src/browser/chrome-mcp.ts:847 passes the caller's signal into createChromeMcpSession(...) for that shared pending attach.
  • extensions/browser/src/browser/chrome-mcp.ts:857 to extensions/browser/src/browser/chrome-mcp.ts:863 makes every concurrent caller await that same pending promise.
  • extensions/browser/src/browser/chrome-mcp.ts:813 to extensions/browser/src/browser/chrome-mcp.ts:824 closes/rejects the created session when that signal aborts.

Reproduction shape

Use setChromeMcpSessionFactoryForTest with a gated fake Chrome MCP session:

  1. Start listChromeMcpTabs("chrome-live", undefined, { signal }) and keep the factory unresolved.
  2. Start a second listChromeMcpTabs("chrome-live") for the same profile.
  3. Abort only the first signal.
  4. Resolve the factory.

Current behavior: both calls reject with the first caller's abort reason.

Expected behavior: the first call rejects, but the second call keeps waiting on the attach and succeeds. If every waiter cancels, the pending attach should be torn down and a later caller should start fresh instead of joining a cancelled pending session.

Proposed fix

Track shared pending Chrome MCP attaches as a small state object with waiter count and an internal abort controller. Caller aborts should release only that caller's wait. The shared attach should be aborted/closed only when the last waiter cancels, and the pending entry should be evicted before teardown so fresh callers cannot join a doomed attach.

Regression tests / proof to add

Add focused coverage in extensions/browser/src/browser/chrome-mcp.test.ts for:

  • one waiter aborting while another waiter remains;
  • every waiter aborting before the factory resolves;
  • every waiter aborting while session.ready is still pending;
  • a fresh caller after an all-waiters-aborted attach;
  • keeping the ready-pending session cached when another waiter remains.

I searched for existing issues/PRs with Chrome MCP pending session abort signal waiter and chrome-mcp pendingSessions createChromeMcpSession abort signal; no overlapping issue or PR showed up.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions