openclaw/openclaw · warning
Playwright connection attempt was superseded.
Error message
Playwright connection attempt was superseded.
What it means
A plain Error thrown when connectEndpoint succeeds but connectionAttempt.cancelled was set to true during the await. The just-opened Browser is retired (tracked for async close) and the connection attempt reports superseded rather than returning a connection the caller no longer wants. This is a cooperative-cancellation mechanism: another caller (closePlaywrightBrowserConnection, a cdpUrl change, or lifecycle teardown) cancelled the in-flight attempt.
Source
Thrown at extensions/browser/src/browser/pw-session-connection.ts:443
return await withManagedProxyForCdpUrl(connectionUrl, () =>
withNoProxyForCdpUrl(connectionUrl, () =>
chromium.connectOverCDP(connectionUrl, { timeout, headers }),
),
);
};
let browser: Browser;
try {
browser = await connectEndpoint(endpoint);
} catch (err) {
if (!isWebSocketUrl(normalized) || endpoint === normalized) {
throw err;
}
browser = await connectEndpoint(normalized);
}
if (connectionAttempt.cancelled) {
connectionAttempt.retired = { browser, cdpUrl: normalized };
void closeTrackedPlaywrightConnection(connectionAttempt.retired).catch(() => {});
throw new Error("Playwright connection attempt was superseded.");
}
const onDisconnected = () => {
const current = cachedByCdpUrl.get(normalized);
if (current?.browser === browser) {
cachedByCdpUrl.delete(normalized);
}
};
const connected: ConnectedBrowser = { browser, cdpUrl: normalized, onDisconnected };
cachedByCdpUrl.set(normalized, connected);
browser.on("disconnected", onDisconnected);
observeBrowser(browser);
return connected;
} catch (err) {
lastErr = err;
if (connectionAttempt.cancelled) {
break;
}
// Don't retry rate-limit errors; retrying worsens the 429.View on GitHub (pinned to 01804a7531)
Solutions
- Retry the connect on a superseded error — by the time it propagates, the cancelling caller has usually finished and a fresh attempt will succeed.
- Avoid issuing connect and close concurrently on the same cdpUrl; serialize through a single owner.
- If this fires during shutdown, treat it as expected and swallow it in the teardown path.
- Check for a long-running stale connection holder that keeps cancelling new attempts.
Example fix
// before
await connectBrowser(cdpUrl); // may throw 'superseded' under concurrent close
// after
try { return await connectBrowser(cdpUrl); }
catch (e) { if (isSupersededError(e)) return await connectBrowser(cdpUrl); throw e; } Defensive patterns
Strategy: retry
Type guard
function isSupersededError(e: unknown): boolean {
return e instanceof Error && /superseded/.test(e.message);
} Try / catch
try { return await connectBrowser(cdpUrl); }
catch (e) {
if (isSupersededError(e)) return await connectBrowser(cdpUrl);
throw e;
} Prevention
- Serialize connect and close on a single cdpUrl owner; do not race them.
- Swallow 'superseded' in teardown/shutdown paths where it is expected.
- Investigate a stale connection holder if superseded errors recur.
When it happens
Trigger: Two callers racing on the same cdpUrl: one connects, the other cancels (e.g. profile reconfiguration, gateway shutdown, explicit connection close). The first to land its connectEndpoint wins; the loser sees cancelled and throws. Also fires when the gateway is shutting down mid-connect.
Common situations: Rapid profile/endpoint switching that tears down a previous connection before its connect completes; concurrent open+close on the same cdpUrl; gateway restart during a slow remote connect; test teardown cancelled an in-flight connect.
Related errors
- CDP connect failed
- No pages available in the connected browser.
- Failed to get targetId for new page
- Action download URL is unavailable
- Failed to read response body for "${url}": ${String(err)}
AI-assisted analysis of openclaw/openclaw@01804a7531 (2026-08-12).
Data as JSON: /api/errors/eaebc8cce8ec9f08.
Report an issue: GitHub.