· Paul Crossland
Open Socket, Wrong Session: Three Browser Control-Plane Gates
Fresh browser and MCP bugs show why route identity, protocol readiness, and feature ownership must be proven before browser commands run.
A remote browser connection can succeed and still attach a crawler to the wrong control surface. A discovery request can lose the reverse proxy's path prefix. An MCP transport can advertise its message endpoint before initialization is complete. Two clients can share one browser context, both start recording, and one receive the other's actions.
None of these failures requires a bad page response. They happen before, or beside, page fetching. They can appear as a 404, invalid tool arguments, an empty recording, or a valid transcript attributed to the wrong client.
The practical thesis is: a browser command should be admitted only after the control plane proves three independent facts—the intended broker route was preserved, protocol initialization completed for this session generation, and the caller owns or safely partitions every stateful feature it will use. An open socket, endpoint event, or shared context proves only transport reachability.
This is reliability and isolation guidance for owned or properly authorized browser work. Control-plane repair must not weaken destination policy, share credentials across principals, or turn a login, challenge, denial, rate limit, or publisher restriction into a reason to seek another session.
Fresh evidence from three control boundaries
| Primary source | Date | What it contributes |
|---|---|---|
| Puppeteer pull request 15435 | 2026-09-08 | Reports that constructing /json/version as an absolute URL path discards a browserURL path prefix used by a reverse proxy or tunnel. The open change appends discovery to the existing pathname and tests a trailing slash plus query string. |
| Crawl4AI issue 2233 | 2026-09-07 | Reproduces concurrent MCP-over-SSE calls reaching the server before its initialize to notifications/initialized sequence completes. The calls receive -32602 Invalid request parameters even though session readiness, not tool arguments, caused rejection. |
| Playwright issue 42608 | 2026-09-07 | Reproduces two MCP clients using --shared-browser-context: the second recording sink replaces the first, client A is told no actions were recorded, and client B receives A's navigation. |
All three were open at publication time. The Playwright reproduction targets development code identified as 1.64.0-next, not a stable release. Test exact deployed builds and sharing modes; do not generalize the reports to every browser service.
Older background includes the original Puppeteer path-prefix issue and the MCP lifecycle specification. Recent Better Fetch posts already cover generic browser control-plane telemetry, network-route separation, principal handoffs, and browser-state checkpoints. Repeating “log how the browser attached” would add little. The new information surplus is an admission predicate: address, readiness, and ownership must all resolve to the same logical browser session before work can fan out.
Route identity is more than host and port
Hosted browsers commonly sit behind gateways that route by pathname. A prefix can identify a tenant, lease, workspace, or tunnel. Puppeteer's browserURL flow first requests a discovery document, then obtains a WebSocket debugger endpoint. If URL construction replaces the supplied pathname with root-level /json/version, the request no longer addresses the same logical route.
A clean 404 is the easy failure. More dangerous is a gateway whose default discovery endpoint returns a valid WebSocket URL for the wrong browser pool. Authentication may also live in a query or gateway header, so route transformation can change reachability and authority.
Build discovery URLs with a reviewed URL operation, not string concatenation or an unexamined leading slash. Preserve the approved scheme, authority, path prefix, and intended query behavior; remove fragments; reject any transformation that escapes the configured route. Then validate the returned WebSocket endpoint against broker policy before connecting. A successful discovery response does not by itself prove session identity.
Do not place raw path leases, query values, headers, or debugger URLs in broad logs. Record an approved route ID, a keyed digest of the normalized prefix, query parameter names, and an endpoint-equivalence verdict.
Endpoint delivery is not protocol readiness
Transport setup and MCP initialization are separate milestones. Receiving an SSE endpoint tells a client where messages can be posted. It does not authorize application calls before capability and version negotiation have settled.
The Crawl4AI report contrasts an SSE path that can pass early posts into the session with a WebSocket handler that explicitly waits for initialization before forwarding later messages. Under concurrent fan-out, tool calls can arrive while the server still considers the session initializing. The resulting -32602 points operators toward malformed tool parameters even though those parameters were not the failed boundary.
Put the primary gate in the client or orchestrator: serialize initialization per session, await the response, send the initialized notification, and only then release tool work. Any compatibility buffer must be bounded, ordered, generation-bound, and discarded on timeout, disconnect, or failed negotiation.
Do not “fix” the race by marking every new session initialized. That removes negotiation rather than synchronizing it. Also separate error classes: control_initializing, session_stale, capability_unavailable, and tool_arguments_invalid demand different retry behavior.
A shared context does not imply shared feature state
Sharing a browser context can preserve an intentional tab, extension, login, or human-approved state across clients. It also means several wrappers may point at one underlying object. A feature implemented with one mutable sink, listener, route handler, trace collector, or output destination may not become multi-client simply because the outer API creates one wrapper per client.
The Playwright report shows that distinction for action recording. Each client-side context can believe it owns a recording, while the shared browser context has one sink. The second start replaces the destination. The first stop then disables the shared recorder. Both calls can return plausible messages while action provenance crosses clients.
Inventory stateful features before shared mode: recording, tracing, HAR capture, interception, downloads, handlers, video, console collection, and teardown. Give each one an explicit policy:
- exclusive lease: reject a second owner until the first releases it;
- partitioned delivery: route events by client and operation identity with tested isolation;
- shared read-only view: all consumers receive declared common evidence without control rights;
- unsupported while shared: require isolated contexts for that feature.
Context sharing is a performance or continuity choice, not an ownership policy. Separate principals or tenants should default to isolated browser contexts unless an explicit authorization and data-isolation design says otherwise.
Issue a control-plane admission receipt
Create one receipt before the first browser command and update it through teardown:
- task, principal, client, broker-route, transport-connection, protocol-session, browser-context, target, and server-deployment generation IDs;
- requested control origin, approved path-prefix digest, discovery suffix, query-name set, response status, and returned-endpoint policy verdict;
- automation, MCP client and server, browser, gateway, and protocol versions;
- timestamps for transport open, initialization request, server response, initialized notification, capability acceptance, and command admission;
- negotiated capabilities and required capability result;
- shared or isolated mode, stateful feature, owner client, lease generation, conflict policy, start result, and stop actor;
- command ID, session phase at receipt, dispatch result, page request start, and artifact IDs produced;
- normalized failure stage:
route,discovery,transport,initialization,ownership,browser_action,page_network, orextraction; - final verdict such as
control_admitted,route_mismatch,not_initialized,feature_conflict,cross_client_delivery,stale_generation, orunknown.
Redact credentials and sensitive browser activity before persistence. Recording and tracing are evidence channels, but they can also contain private URLs, form actions, and session-derived data. The receipt should identify the restricted artifact, not copy its contents into ordinary telemetry.
A two-client admission matrix
Run the exact gateway, client libraries, MCP transport, sharing mode, and browser build against owned fixtures:
- Test root and path-prefixed browser URLs with and without trailing slashes and approved query parameters. Place a harmless decoy at root
/json/version; require the client to reach the prefixed marker, not merely any valid discovery document. - Return a WebSocket endpoint for a different synthetic lease. Require endpoint-policy rejection before attachment.
- Delay initialization, then release several tool calls concurrently. Require zero tool execution before admission and a readiness-specific outcome for rejected work.
- Repeat over every supported transport. Equivalent lifecycle rules must not depend on whether messages use SSE, WebSocket, or another adapter.
- Restart the server while clients retain old session identifiers. Require stale-generation rejection and fresh negotiation rather than accidental attachment to a replacement session.
- Connect two clients to one shared browser context. Start the same stateful feature concurrently; require explicit exclusion or correctly partitioned outputs. Verify every recorded action belongs to its initiating client.
- Disconnect the feature owner during an action. Require bounded cleanup and no inheritance by the next client; repeat in isolated mode and across synthetic principals.
- Navigate an owned page only after control admission. Prove an earlier failure creates no page request and cannot be relabeled as a source timeout.
Isolation costs memory and can discard useful continuity. Feature leases add scheduling and cleanup complexity. Startup buffering may hide a broken client. Choose deliberately; never let a successful socket decide the trade-off.
The operator decision rule
When remote browser work fails before useful page evidence appears, locate the first unproven gate. If the discovery route changed, stop before the WebSocket and repair URL composition or broker policy. If transport opened but negotiation did not settle, classify readiness rather than blaming tool arguments or the target site. If the protocol session is ready but a stateful feature has another owner, reject, isolate, or partition it before recording any action.
Only after route identity, protocol readiness, and feature ownership agree should a browser command be eligible to create fetch evidence. That one admission rule turns misleading 404s, -32602s, empty recordings, and cross-client transcripts into bounded control-plane failures—before they become wrong crawls attributed to the page.