Skip to content
Puppeteer · error message

Puppeteer: Session closed. Most likely the page has been closed.

Permanent - do not retryProtocol, session & connection

Last verified

The exact message
Error: Protocol error (Runtime.callFunctionOn): Session closed. Most likely the page has been closed.

What it means

Each page has its own DevTools session. When the page is closed, or the browser exits, that session is detached, and any command sent afterwards is rejected with this message. The method in parentheses is the command that was attempted, for example Runtime.callFunctionOn for an evaluate or Page.navigate for a goto.

In practice the page was closed by your own code or by the browser crashing. Missing await keywords are the classic cause: a function returns, browser.close() runs in a finally block, and an un-awaited page.evaluate() fails a moment later. Pages that call window.close() (OAuth popups) and memory-starved containers that kill renderer processes produce the same error.

What is not wrong: the evaluated code or selector. The target simply no longer exists.

What it looks like

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  page.on('close', () => console.warn('page closed'));
  await page.goto('https://example.com/');
  const title = await page.evaluate(() => document.title); // awaited
  console.log(title);
} finally {
  await browser.close();
}

Why it happens

  • An un-awaited Puppeteer call is still running when browser.close() or page.close() executes.
  • Several tasks share one page and one of them closes it.
  • The page closed itself (popup or OAuth window calling window.close()).
  • The renderer or browser crashed, often from memory pressure in containers.

How to fix it

  1. Await every Puppeteer call and enable a no-floating-promises lint rule.
  2. Close pages and browsers only after all work using them has resolved.
  3. Give each concurrent task its own page, or its own browser context with browser.createBrowserContext().
  4. Listen for page.on('close') and browser.on('disconnected') to detect unexpected closes and log them.
  5. Increase container memory or reduce concurrency if the browser crashes under load.

How to stop it recurring

Structure scripts so the browser's lifetime visibly encloses every call, usually with try/finally and awaited calls only. Isolate concurrent work in separate pages or contexts. Monitor memory in containers, since renderer crashes show up as closed sessions rather than as obvious out-of-memory errors.

Official documentation: pptr.dev/api/puppeteer.page.close. See all Puppeteer error messages.

Related errors

Definitions

Quick answers

What does "Session closed. Most likely the page has been closed." mean in Puppeteer?

Each page has its own DevTools session. When the page is closed, or the browser exits, that session is detached, and any command sent afterwards is rejected with this message. The method in parentheses is the command that was attempted, for example Runtime.callFunctionOn for an evaluate or Page.navigate for a goto . In practice the page was closed by your own code or by the browser crashing.

How do I fix "Session closed. Most likely the page has been closed."?

1. Await every Puppeteer call and enable a no-floating-promises lint rule. 2. Close pages and browsers only after all work using them has resolved. 3. Give each concurrent task its own page, or its own browser context with browser.createBrowserContext(). 4. Listen for page.on('close') and browser.on('disconnected') to detect unexpected closes and log them. 5. Increase container memory or reduce concurrency if the browser crashes under load.

Run browser automation you can watch and take over

Autonoly runs each job in a real cloud Chromium browser driven by Playwright. Timeouts and detached elements still happen; the difference is that you can watch the run live, see the step that failed, and take control of the browser to finish it.

Also see Live browser control.