Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix Now×
Skip to content
browser debugging

How to Fix Puppeteer Protocol Error: Page.navigate Target Closed

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

“Protocol error (Page.navigate): Target closed” means Puppeteer lost the page target or DevTools protocol session before navigation finished. The message does not identify one guaranteed root cause. Start by checking asynchronous ordering and cleanup, then investigate browser disconnects or crashes, launch compatibility, and navigation-specific waits. Do not begin with blind retries or arbitrary Chrome flags.

What the error actually means

A call such as await page.goto(url) sends a Page.navigate command through Chrome DevTools Protocol. “Target closed” means the target (normally a page or frame’s browser context) disappeared, or Puppeteer’s connection to it ended, before the command completed.

That target can disappear because your code closed it, a worker or signal handler shut down the browser, another component owns and closes a shared browser, or the browser process disconnected or crashed. A click-triggered navigation can also be synchronized incorrectly. The error text alone cannot distinguish these cases. Puppeteer issue #7455 recorded the same message in a basic launch, new-page, navigation and screenshot example; it did not establish a single root cause.

Treat the problem as two main branches:

  • Page/code lifecycle: un-awaited work, early returns, timeout races, page or context closure, worker shutdown, or incorrect shared-browser ownership.
  • Browser/protocol/process: browser disconnect, crash, incompatible executable, missing environment dependency, or a protocol connection that ended unexpectedly.

When a click or request handler controls navigation, there is a narrower synchronization branch: wait for the navigation caused by that action and finish every intercepted request.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

1. Check for a cleanup race before changing Chrome settings

The first check is whether navigation is still pending when cleanup runs. This common mistake is equivalent to:

page.goto('https://example.com');
await browser.close();

Because page.goto() is not awaited, browser.close() can close the target while the protocol command is in flight. The same race can be hidden in a helper function, a test hook, a timeout, or a finally block.

Audit the entire failing path

  • Await page.goto(), page.click(), screenshots, PDF generation and any helper that must finish before cleanup.
  • Search for page.close(), context.close() and browser.close() before the navigation promise resolves.
  • Inspect early returns and thrown errors that enter finally while another promise is still running.
  • Check Promise.race timeouts. Cancelling the controlling task does not automatically make an already-running navigation safe.
  • Check request cancellation, worker shutdown hooks, test teardown and process signal handlers such as SIGTERM.
  • If multiple requests share one browser, verify that one request cannot close a browser still used by another.

Use a lifecycle-safe baseline while diagnosing:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  const response = await page.goto('https://example.com');
  console.log('HTTP status:', response?.status());
} finally {
  await browser.close();
}

This pattern guarantees that the normal navigation promise settles before normal cleanup. It cannot repair a browser that crashes, an externally closed page, or a process that is killed.

2. Synchronize a click that triggers navigation

page.goto() already represents a direct navigation. Do not add a second, unrelated navigation wait around every goto. Use page.waitForNavigation() when an action such as a click causes the navigation indirectly, and start both promises together:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const [response] = await Promise.all([
  page.waitForNavigation(),
  page.click('a.my-link'),
]);

console.log('Final response:', response?.status());

Starting the wait before the click avoids missing a fast navigation. If redirects occur, Puppeteer resolves with the response for the last redirect. Anchor or History API navigation may resolve with null, so do not assume a response object always exists. This synchronization prevents a timing mistake; it does not resurrect a target that has already been closed.

3. Determine whether the browser disconnected or crashed

If promise ordering is correct, collect evidence from the browser and protocol rather than guessing. Attach a disconnect listener early:

const browser = await puppeteer.launch({ dumpio: true });

browser.on('disconnected', () => {
  console.error('Puppeteer disconnected from the browser');
});

dumpio: true forwards the browser process’s stdout and stderr, which can reveal a launch failure or crash that appears in application code only as “Target closed.” For DevTools Protocol logging, Puppeteer documents the NODE_DEBUG="puppeteer:*" environment setting. Enable it for a focused reproduction and redact cookies, authorization headers, URLs containing secrets and page content before sharing logs.

Puppeteer’s debugging documentation also describes inspecting pending protocol callbacks through browser.debugInfo.pendingProtocolErrors. A pending callback at disconnect time helps establish that the protocol session ended while work was outstanding, although it does not by itself identify why the browser ended.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture page-side clues too:

page.on('console', message => {
  console.log(`[page:${message.type()}]`, message.text());
});

page.on('pageerror', error => {
  console.error('Page error:', error);
});

Run a minimal reproduction in headful mode or with a slow-motion setting when visual timing matters. Puppeteer’s debugging guide emphasizes that there is no single method for every issue because Puppeteer spans browser processes, network requests and Web APIs.

4. Verify who owns the browser and what cleanup does

browser.close() shuts everything down

browser.close() closes the browser and its associated pages. Calling it from a request handler, test hook or worker-finally block can invalidate another task’s page if the browser is shared.

browser.disconnect() leaves Chrome running

browser.disconnect() only disconnects Puppeteer from the browser. The browser and its pages continue running. This is useful when another process owns Chrome, but it also means a later owner may still be responsible for cleanup.

Make ownership explicit

  • Create one browser per isolated job, or implement a documented pool with reference counting.
  • Let only the owner call browser.close().
  • Keep signal handlers from closing a shared browser while jobs are active; coordinate shutdown and await in-flight work.
  • Log a job ID, page URL, browser PID (when available), close/disconnect event and cleanup path.

5. Check Puppeteer, browser and deployment compatibility

Record the exact Node.js version, Puppeteer version, Chrome or Chromium version, operating system, container image and launch configuration. Also record whether Puppeteer launched its bundled browser, a system executable, or a remotely connected browser.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Puppeteer’s LaunchOptions documentation (shown as version 25.12.0 in the referenced page) states: “Note that Puppeteer is only guaranteed to work with the bundled browser.” A custom executable is not automatically wrong, but a mismatch becomes more plausible when logs show launch failure, an immediate browser exit or protocol-version errors. Investigate those facts instead of assuming that every target-closed report has the same cause.

The Puppeteer troubleshooting page is published under a /next/ path, so environment workarounds can be version-sensitive. Check that guidance against your installed release before applying it. It discusses issues such as missing system dependencies, browser-cache problems, sandbox or AppArmor restrictions and other deployment-specific failures.

Do not add --no-sandbox as a routine fix. Disabling the sandbox is strongly discouraged in the official troubleshooting guidance and should only be considered under a deliberate, secured deployment design with an understood risk.

6. Inspect request interception if navigation stalls

When request interception is enabled, every intercepted request must be completed with exactly one appropriate action: continue(), respond() or abort(). An unhandled request can stall navigation. Puppeteer notes that intercepted requests remain paused unless handled or satisfied by cache.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.setRequestInterception(true);
page.on('request', request => {
  if (request.resourceType() === 'image') {
    request.abort().catch(() => {});
  } else {
    request.continue().catch(() => {});
  }
});

Use this as a navigation-hang branch, not as a universal explanation for “Target closed.” Make sure the handler is installed before the navigation and that exceptions in the handler cannot leave requests unresolved.

Common symptoms and targeted fixes

Symptom Likely branch Targeted action
Error appears immediately after a function returns Missing await or early cleanup Await navigation and inspect finally, timeout and signal paths.
Several pages fail at once and a disconnect event fires Browser process exited or crashed Enable dumpio, collect browser stderr and inspect protocol logs.
Only click-driven navigation fails Action/navigation race Use Promise.all([page.waitForNavigation(), page.click(...)]).
Only a custom Chrome or container fails Launch or environment mismatch Record versions and executable path; check release-specific troubleshooting.
Failures began after enabling interception Paused request Ensure every request reaches continue, respond or abort.
One worker fails while others keep running Shared-browser ownership Find which worker called browser.close() and isolate ownership.

What not to do

  • Do not retry blindly. A retry cannot reopen a closed target and may hide a deterministic lifecycle race.
  • Do not copy random Chrome flags. Flags can mask the symptom, weaken security or create a different environment.
  • Do not reinstall packages as a first response. Establish whether the browser is closing and capture versions first.
  • Do not claim redirects or Linux shared memory are always responsible. They are hypotheses only when logs and reproduction support them.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How to report a useful reproduction

Reduce the failing flow to launch, page creation, one navigation and the smallest action that triggers the error. Include:

  • Node.js, Puppeteer and Chrome/Chromium versions.
  • Operating system, container or CI details and whether Chrome is bundled, system-installed or remote.
  • The launch options, excluding credentials.
  • A complete promise/cleanup path, including signal handlers and test hooks.
  • Redacted dumpio output, disconnect timing and relevant protocol logs.
  • Whether request interception, custom headers, proxies, timeouts or shared-browser pooling is enabled.

Or skip the browser setup

If your goal is simply to obtain a clean website image rather than maintain Chrome yourself, ScreenshotNeo provides a GET request that returns PNG, JPEG or WebP (and can capture PDF). It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.

See the parameter reference in the ScreenshotNeo documentation. A one-call example:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Equivalent Python:

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)

Equivalent Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Every feature is available on every plan. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for the free plan if you want to avoid managing a browser process.

Best Value
The SQL Programming Language: .
  • Used Book in Good Condition

FAQ

Does this error prove that my selector or URL is wrong?

No. A bad URL or selector can cause a different failure, but “Target closed” specifically reports that the target or protocol session was gone before the operation completed.

Should I use waitForNavigation() with every page load?

No. Use it for an action that triggers navigation, such as a click. A direct page.goto() already provides the navigation promise you should await.

Is browser.disconnect() a safer replacement for browser.close()?

They serve different ownership models. Disconnect leaves Chrome and pages running; close shuts them down. Choose based on which component owns the browser and when all work has completed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Does this error prove that my selector or URL is wrong?

No. A bad URL or selector can cause a different failure, but “Target closed” specifically reports that the target or protocol session was gone before the operation completed.

Should I use waitForNavigation() with every page load?

No. Use it for an action that triggers navigation, such as a click. A direct page.goto() already provides the navigation promise you should await.

Is browser.disconnect() a safer replacement for browser.close()?

They serve different ownership models. Disconnect leaves Chrome and pages running; close shuts them down. Choose based on which component owns the browser and when all work has completed.

The Bottom Line

Find out who closed the target before changing flags: verify promise ordering and ownership, then use disconnect events, browser output, protocol logs and version data to distinguish a lifecycle race from a browser or environment failure.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.