microsoft/playwright · error · Error
Root session cannot be closed
Error message
Root session cannot be closed
What it means
Thrown by CRConnection.detach() (crConnection.ts:180) when this._parentSession is falsy. The root browser-level session has no parent to detach from, so Target.detachFromTarget cannot apply. Root sessions are obtained from _clientRootSession() and are not meant to be detached by users.
Source
Thrown at packages/playwright-core/src/server/chromium/crConnection.ts:180
callback.resolve(object.result);
}
} else if (object.id && object.error?.code === -32001) {
// Message to a closed session, just ignore it.
} else {
assert(!object.id, object?.error?.message || undefined);
Promise.resolve().then(() => {
if (this._eventListener)
this._eventListener(object.method!, object.params);
(this.emit as any)(object.method as any, object.params);
});
}
}
async detach() {
if (this._closed)
throw new Error(`Session already detached. Most likely the page has been closed.`);
if (!this._parentSession)
throw new Error('Root session cannot be closed');
// Ideally, detaching should resume any target, but there is a bug in the backend,
// so we must Runtime.runIfWaitingForDebugger first.
await this._sendMayFail('Runtime.runIfWaitingForDebugger');
await this._parentSession.send('Target.detachFromTarget', { sessionId: this._sessionId });
this.dispose();
}
dispose() {
this._closed = true;
this._connection._sessions.delete(this._sessionId);
this._rejectPendingCallbacks(`Internal server error, session closed.`);
}
private _rejectPendingCallbacks(message: string) {
for (const callback of this._callbacks.values()) {
callback.error.setMessage(message);
callback.error.type = this._crashed ? 'crashed' : 'closed';
callback.error.logs = this._connection._browserDisconnectedLogs;View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Do not detach the root session; only detach sessions created via newCDPSession(page|frame).
- Before detaching generically, check that the session has a parent (skip the root).
Defensive patterns
Strategy: validation
Validate before calling
// Only detach sessions you created via newCDPSession. // If walking sessions generically, skip the root: // root sessions have no parent — do not call detach() on them. const isChildSession = (s: CDPSession) => /* track via your own registry */ true; if (isChildSession(session)) await session.detach();
Prevention
- Do not detach browser-level/root CDP clients.
- Maintain an explicit list of sessions you opened and only detach those.
- Treat 'Root session cannot be closed' as a programming error, not a runtime condition.
When it happens
Trigger: Calling detach() on the browser's root CDP client; generic session-walking tooling that detaches every session it finds, including the root; reaching the root after a parent chain was nulled.
Common situations: Internal tooling that lists _connection._sessions and detaches each; user code that grabbed the browser-level client for an unrelated command and then tries to detach.
Related errors
- Session already detached. Most likely the page has been clos
- Connecting to workers is only supported in Chromium.
- Could not connect to ${channel}.\n${remoteDebuggingHint(chan
- Playwright manages remote debugging connection itself.
- Connecting to ${channel} by channel name is not supported on
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/46f58e78ee4ec1a6.
Report an issue: GitHub.