ScreenshotNeo

BlogHow-to

How to Close a Puppeteer Connection Transport

Close a custom Puppeteer transport with its synchronous close() method. Learn when to use browser.disconnect(), browser.close(), or close a page or context instead.

By the ScreenshotNeo team4 October 20266 min read

To close an implementation of Puppeteer’s ConnectionTransport, call transport.close(). It is synchronous and returns void. If you mean the browser session rather than the transport implementation, use await browser.disconnect() to detach Puppeteer while leaving the browser running, or await browser.close() to shut down the browser and its pages.

These methods act at different layers. Pick the narrowest operation that matches what should stop, and do not await transport.close(): the documented interface method does not return a promise. See the ConnectionTransport API reference and Browser API reference.

Choose what to close

What should stop? Call Effect Return type
Your transport implementation transport.close() Invokes the implementation’s transport shutdown hook. The interface does not prescribe its internal cleanup behavior. void
Puppeteer’s connection to a browser that should remain running await browser.disconnect() Detaches Puppeteer; the browser process remains running. Promise
The browser Puppeteer manages await browser.close() Closes the browser and its associated pages. Promise
A non-default browser context await context.close() Closes that context and its pages. The default context cannot be closed. Promise
One page await page.close() Closes the page; an optional runBeforeUnload setting controls whether to run its before-unload handlers. Promise

Sources: ConnectionTransport, Browser, BrowserContext, and Page.close().

Close a custom ConnectionTransport

If you own the object implementing Puppeteer’s transport interface, call its close() method when your transport lifecycle is finished:

transport.close();

The interface also defines send(message) and optional onclose and onmessage callbacks. Its type signature establishes that close() is synchronous; it does not specify whether the implementation should close a socket, notify listeners, be idempotent, or release other resources. Define those details in your implementation’s own lifecycle contract. Do not assume the interface itself handles them.

Minimal TypeScript implementation shape

This example shows the interface shape and a synchronous close hook. The socket and event behavior are deliberately represented as implementation-owned details; wire them to the transport you actually use.

import type { ConnectionTransport } from 'puppeteer-core';

class MyTransport implements ConnectionTransport {
  onmessage?: (message: string) => void;
  onclose?: () => void;

  send(message: string): void {
    // Send through your underlying transport.
  }

  close(): void {
    // Synchronously initiate your transport's shutdown.
    // Apply your implementation's own listener and resource policy here.
  }
}

const transport = new MyTransport();
transport.close();

Use the version of the Puppeteer type definitions installed by your project. If the compiler reports a mismatch, check the installed package’s interface: the API references surfaced in research span several documentation versions.

Disconnect or close the browser

For a browser session, use Puppeteer’s browser object. Disconnect when another process or controller should continue using the browser; close when Puppeteer should shut the browser down.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();

// ...use the page...

// Detach Puppeteer, leaving the browser process running:
await browser.disconnect();

// Or, in a separate run where Puppeteer should stop the browser:
// await browser.close();

Choose one shutdown path for a given lifecycle. After disconnect(), Puppeteer is detached; do not treat that operation as browser-process cleanup. browser.close() closes the browser and associated pages.

Close only a context or page

When the browser should stay available but a particular group of pages or one page should end, close that smaller scope:

const context = await browser.createBrowserContext();
const contextPage = await context.newPage();

// Close this context and all its pages. Do not use this on the default context.
await context.close();

const page = await browser.newPage();
// Close just this page. The option is optional.
await page.close();
// Equivalent explicit form:
// await page.close({ runBeforeUnload: false });

For a page close that should run before-unload handlers, pass { runBeforeUnload: true }. The page API documents the option as optional. Context shutdown applies to a non-default context and its pages; the default context cannot be closed.

Cleanup when work can fail

Put browser cleanup in a finally block so an exception during page work does not skip the chosen shutdown operation. This example owns the browser process, so it closes it:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();

try {
  const page = await browser.newPage();
  await page.goto('https://example.com');
  console.log(await page.title());
} finally {
  await browser.close();
}

If the browser is shared or must remain running, make the cleanup policy explicit and use browser.disconnect() instead. For a custom transport, call its synchronous close() according to its implementation contract. Avoid placing both browser shutdown and transport shutdown in cleanup unless your ownership model requires both and documents their order.

Common errors and fixes

Symptom Likely cause Fix
Code uses await transport.close() The transport interface declares close(): void; awaiting it does not make transport cleanup asynchronous. Call transport.close() directly. If your own implementation needs asynchronous cleanup, expose and await a separate method in its own API rather than changing the documented interface assumption.
The browser is still running after cleanup browser.disconnect() detaches Puppeteer and intentionally leaves the browser process running. Use await browser.close() when Puppeteer should shut down the browser it manages.
Other pages stop when one context closes context.close() closes all pages in that context. Close only the intended page with page.close(), or put unrelated pages in separate non-default contexts.
Attempting to close the default context fails Puppeteer documents that the default browser context cannot be closed. Close individual pages in the default context, or create and close a separate browser context.
Transport resources or listeners remain active The interface signature does not define the implementation’s socket, listener, or resource cleanup policy. Inspect the concrete transport implementation and ensure its close() hook performs the cleanup your transport requires.
A transport type does not match the example The project may use a different Puppeteer package or version. Check the installed package’s ConnectionTransport definition and the matching versioned API reference.

Performance, reliability, and cost

The documented distinction is lifecycle behavior, not a performance ranking: transport close is a synchronous interface hook, while browser, context, and page methods return promises. Choose the smallest scope that ends the resources you own. This reduces accidental disruption in shared browser processes and makes cleanup intent easier to reason about.

For reliability, give each browser, context, page, and custom transport a clear owner and one cleanup path. Use try/finally around work that can throw, and define whether repeated transport closure is supported by your concrete implementation. The interface reference does not promise idempotence or specify cleanup ordering.

Cost depends on how you run and host the browser; the cited API documentation does not establish pricing or resource benchmarks. If your goal is simply to capture a website screenshot rather than manage a Puppeteer browser lifecycle, an API can avoid maintaining browser setup.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request can return a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for request options.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each of those steps can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server offers take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is on every plan. Sign up for 1,000 free screenshots a month, with no card.

FAQ

Is ConnectionTransport.close() asynchronous?

No. The interface signature is close(): void, so call it synchronously.

Does browser.disconnect() kill Chrome?

No. It detaches Puppeteer and leaves the browser process running.

Can I close just one Puppeteer page?

Yes. Use await page.close(); it returns a promise and optionally accepts runBeforeUnload.

Can I close the default BrowserContext?

No. The documented API says the default context cannot be closed.

Should application code call Connection.dispose() instead?

The API reference provides its void signature but no lifecycle guidance in the research available here. Use the documented transport and browser methods that match the resource you intend to close.