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

  1. Guard detach() with the session.detached (or isClosed()) flag before calling.
  2. Track detach completion in your own boolean and skip re-detach.
  3. Use a single cleanup path (e.g. using/dispose-style helper) to avoid double detach.
  4. 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

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


AI-assisted analysis of puppeteer/puppeteer@d484e21c17 (2026-08-12). Data as JSON: /api/errors/d90d51356c9d755e. Report an issue: GitHub.