puppeteer/puppeteer · warning · Error
Session already detached. Most likely the ${this.#targetType
Error message
Session already detached. Most likely the ${this.#targetType} has been closed. What it means
Thrown by CdpSession.detach when called on a session whose detached flag is already true. Detach is a one-shot operation: once a session has been detached (either by a prior detach() call or by an onClosed path), calling detach() again is a programmer error rather than a recoverable state.
Source
Thrown at packages/puppeteer-core/src/cdp/CdpSession.ts:157
object.error.message,
);
}
} else {
this.#callbacks.resolve(object.id, object.result);
}
} else {
assert(!object.id);
this.emit(object.method, object.params);
}
}
/**
* Detaches the cdpSession from the target. Once detached, the cdpSession object
* won't emit any events and can't be used to send messages.
*/
override async detach(): Promise<void> {
if (this.detached) {
throw new Error(
`Session already detached. Most likely the ${this.#targetType} has been closed.`,
);
}
await this.#connection.send('Target.detachFromTarget', {
sessionId: this.#sessionId,
});
this.#detached = true;
}
/**
* @internal
*/
onClosed(): void {
this.#callbacks.clear();
this.#detached = true;
this.emit(CDPSessionEvent.Disconnected, undefined);
}
View on GitHub (pinned to d484e21c17)
Solutions
- Guard detach() with the session.detached (or isClosed()) flag before calling.
- Track detach completion in your own boolean and skip re-detach.
- Use a single cleanup path (e.g. using/dispose-style helper) to avoid double detach.
- Wrap detach() in a try/catch that ignores the 'already detached' error if idempotency is required.
Example fix
// before
await session.detach();
// ...later, in cleanup
await session.detach(); // throws 'Session already detached'
// after: guard with the detached flag
if (!session.detached) {
await session.detach();
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!session.detached) {
await session.detach();
} Type guard
function isDetached(session: import('puppeteer-core').CDPSession): boolean {
// CDPSession exposes .detached (or use a tracked boolean)
return Boolean((session as any).detached);
} Try / catch
try {
await session.detach();
} catch (e) {
if (!/Session already detached/.test((e as Error).message)) throw e;
// already detached; nothing to do
} Prevention
- Track detach completion in your own boolean.
- Centralize detach in a single cleanup path.
- Guard detach() with the detached flag before calling.
When it happens
Trigger: Calling session.detach() twice; calling detach() after the session was already closed by the target going away; a finally/cleanup block that detaches unconditionally and runs after an earlier detach succeeded.
Common situations: Cleanup hooks that detach without checking state; reusing a session object across try/catch blocks where detach may already have run; teardown logic that races with the browser closing the session.
Related errors
- Protocol error (${method}): Session with given id not found.
- sessionFactory is not initialized
- ${PROCESS_ERROR_EXPLANATION}\nError cause: ${isErrorLike(err
- Must start a new Touch first
- Protocol error (${method}): Session closed. Most likely the
AI-assisted analysis of puppeteer/puppeteer@d484e21c17 (2026-08-12).
Data as JSON: /api/errors/d90d51356c9d755e.
Report an issue: GitHub.