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

In Puppeteer, a timeout does not automatically mean the Chrome process will exit. A launch timeout limits browser startup, and a navigation or wait timeout rejects that particular operation; neither is a deadline for the whole browser session. To stop a job after a fixed time, arrange cancellation and make sure browser cleanup runs on both success and failure. The example below uses Puppeteer; other Chrome drivers need their own cancellation and process-cleanup APIs.

First identify what timed out

There are several different clocks in a headless-browser script. The fix depends on which one expired and what you need to stop: a browser that is still starting, one page operation, or the entire job.

Timeout scope What expires Does that alone promise Chrome will exit?
Browser startup Puppeteer waiting for Chrome to launch No. It limits startup waiting, not the later browser session.
Navigation or wait A page operation such as navigation or waiting for a selector No. The operation can reject while the browser remains open.
Whole task Your application-defined maximum job duration Only if your code cancels or closes the browser and awaits cleanup.

This distinction explains the common symptom: the script reports a timeout, but a Chrome child process remains. A rejected promise reports that an operation did not finish within its limit; it is not, by itself, a process kill.

Use a whole-task deadline and close the browser

In Puppeteer, use an AbortController signal for a deadline that includes browser startup, and put browser closure in a finally block. Keep a separate timeout on navigation or other waits so a stalled page operation can fail earlier than the overall job deadline.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const puppeteer = require('puppeteer');

const url = 'https://example.com';
const jobTimeoutMs = 30_000;
const navigationTimeoutMs = 20_000;
const controller = new AbortController();
const deadline = setTimeout(() => controller.abort(), jobTimeoutMs);
let browser;

try {
  browser = await puppeteer.launch({ signal: controller.signal });
  const page = await browser.newPage();
  await page.goto(url, { timeout: navigationTimeoutMs });

  // Perform the rest of the browser task here.
} catch (error) {
  // Handle launch cancellation, navigation timeout, or task failure.
  console.error('Browser job failed:', error);
} finally {
  clearTimeout(deadline);
  if (browser) {
    await browser.close();
  }
}

Run this inside an async function or an environment that supports top-level await. Install Puppeteer in the project using your usual package manager before running it. Replace the URL and the task body with your own. The two sample durations are choices for this example, not universal recommendations.

What each part does

  • AbortController provides a signal Puppeteer accepts during launch. When the deadline fires during startup, the launch can be aborted rather than waiting indefinitely.
  • page.goto() has its own timeout. If navigation exceeds that limit, its promise rejects; the catch handles the failure and finally closes an already-created browser.
  • browser.close() is the cleanup step for a browser that successfully launched. Awaiting it lets the script wait for Puppeteer’s close operation instead of abandoning it.
  • clearTimeout() prevents the deadline callback from firing after the job has finished.

Puppeteer’s documented launch timeout defaults to 30,000 milliseconds, and setting it to 0 disables that startup timeout. Wait options also document a 30,000-millisecond default and 0 to disable the respective wait timeout. These are separate settings: disabling one does not disable or configure the other. Prefer an explicit application deadline when the requirement is “this complete job must not run longer than N.”

Set the right timeout for each operation

Limit browser startup

Use Puppeteer’s launch timeout option when the problem is that Chrome takes too long to start. That setting controls how long Puppeteer waits for startup; it does not put a cap on page loads, scripts, or the full browser session. A startup timeout can prevent a launch from waiting beyond its limit, but use the signal-and-cleanup pattern when you need an overall job deadline.

Limit navigation and page waits

Set a timeout on the specific navigation or wait that might stall. For example, page.goto(url, { timeout: 20_000 }) bounds that navigation. Other Puppeteer wait APIs accept wait options with their own timeout behavior. Treat the resulting rejection as an error your application must handle; do not assume it closes the browser automatically.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

Limit the entire job

An outer deadline should cover all work that belongs to the job, not just the initial navigation. Arrange what happens when it fires: cancel operations where the driver supports cancellation, then close the browser and await cleanup. Puppeteer’s launch API accepts an AbortSignal; its launcher handles an aborted signal by killing the browser process. The exact timing and behavior still depend on whether the signal fires during launch or after a browser has been returned, so retain explicit browser cleanup for the post-launch case.

Why Promise.race is not enough

A common shortcut is to race a browser task against a timer:

await Promise.race([
  doBrowserWork(),
  new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timed out')), 30_000)
  )
]);

This makes the caller stop waiting when the timer wins. It does not cancel the losing promise or terminate Chrome. The browser operation may continue in the background. If you use a race, its timeout branch must also trigger the driver’s cancellation path or close the browser, and your code must still await cleanup. For Puppeteer, an abort signal and a finally block make the intended lifecycle clearer than relying on a race alone.

Make cleanup reliable on every path

  • Create the browser variable outside the try block so cleanup can see it after a later failure.
  • Put cleanup in finally, not only after the successful task body. Navigation errors, selector timeouts, and application exceptions should also reach it.
  • Await browser.close(). If your surrounding function is not asynchronous, make it asynchronous or otherwise ensure the close promise is handled.
  • Clear deadline timers when the task has settled. Otherwise, a late timer can abort or disrupt work that has already completed.
  • Do not treat a caught timeout as successful completion. Record or return a failure result as appropriate for your application, while still running cleanup.

For an application that manages multiple pages or browsers, track which browser belongs to which job. A job deadline should close only the resources that job owns; indiscriminately killing every Chrome process on a shared host can interrupt unrelated work.

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.

Troubleshoot a Chrome process that remains

Confirm which timeout fired

Log the stage before each operation—launch, navigation, selector wait, or later task work—and include the error in your logs. A launch timeout, a page-wait timeout, and an outer deadline need different responses. If only navigation timed out, check that execution proceeds through the error handling and cleanup path.

Check whether the cleanup path runs

Put a temporary log immediately before and after the awaited browser.close(). If the first appears but the second does not, inspect whether close is failing or hanging and handle that failure at the job boundary. If neither appears after an error, look for an unhandled rejection, an early return, or a code path that bypasses the finally block.

Check container process reaping

In Docker, a process that has exited may remain visible as a zombie if the container does not reap child processes properly. Puppeteer’s troubleshooting guidance calls out PID 1 behavior and recommends checking an init process such as dumb-init for zombie Chrome processes. That is a container lifecycle issue, distinct from whether the Puppeteer timeout rejected.

Check Cloud Run CPU allocation

If browser work continues in the background after an HTTP response on Google Cloud Run, inspect the service’s CPU allocation behavior. Puppeteer’s troubleshooting guidance notes that CPU may be disabled after a response, making later browser work appear extremely slow. Either finish browser work before responding or configure CPU allocation for background work.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

Do not transplant Puppeteer APIs to another driver

For direct Chrome command-line use, Playwright, Selenium, or a separate process manager, do not assume Puppeteer’s AbortSignal, browser.close(), or timeout defaults apply. Consult that tool’s cancellation and child-process cleanup behavior. There is no stack-independent timeout setting established here that guarantees every Chrome process exits.

Or skip the browser setup

If your goal is to capture a website screenshot or PDF rather than run arbitrary browser automation, ScreenshotNeo provides a one-request API at ScreenshotNeo. For example, this cURL call saves a WebP screenshot of Stripe:

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

See the ScreenshotNeo API documentation for request options and response details. Cookie banners are accepted or removed before capture, and newsletter popups and chat widgets are removed; those steps can be turned off. 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 lets AI agents use screenshot and PDF-capture tools. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. This is for screenshot capture, not a replacement for general-purpose Puppeteer automation.

Sign up free for 1,000 screenshots a month with no card.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance and reliability considerations

Timeouts are bounds, not speed improvements. A shorter navigation timeout can make a job fail sooner, but it cannot make a slow page load faster. Choose values based on the work and environment, and distinguish the operation limit from the overall deadline so logs explain where time was spent.

Closing the browser matters for resource use as well as process hygiene: an abandoned browser may continue doing work after the caller has given up. For repeated jobs, consistently closing browser instances on both success and error makes resource ownership easier to reason about. In containers and serverless hosts, also account for the host’s child-process and CPU lifecycle; application-level timeout code cannot fix a host that fails to reap processes or suspends background work.

FAQ

Does Puppeteer’s launch timeout close Chrome after the script runs too long?

No. It bounds browser startup. Set an application-level deadline and handle browser cleanup for the duration of the task.

Does a timed-out page.goto() kill the browser?

No. It can reject the navigation promise while the browser remains available. Handle the rejection and close the browser if the job should end.

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

Should I set a timeout to zero?

Only if you intentionally want that particular Puppeteer startup or wait timeout disabled and have another suitable way to bound the work. A zero value does not provide a whole-task deadline.

Can I safely kill every process named Chrome?

Not on a host where other jobs may use Chrome. Prefer the browser or process-tree cleanup for the instance your job started, especially on shared hosts.

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.