What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Prevent Puppeteer memory leaks by giving every page, context, and browser a clear owner, then closing each owned resource in a finally block. Handle timeouts by identifying which operation failed—launch, navigation, selector/action, or DevTools Protocol—before changing its limit. A larger timeout can accommodate a genuinely slow operation, but it cannot fix a leak, a missing selector, or an unhealthy Chromium process.
Start with explicit ownership and cleanup
A Puppeteer leak is often a lifecycle bug: a job creates a page, context, listener, or protocol session, then an error path skips its cleanup. Define whether each job owns a page, a browser context, or an entire browser process. Close what the job owns, and close a shared browser only when its process owner shuts down.
Use a page per job and close it in finally
A page-per-job pattern is straightforward for a single job using a shared browser. The finally path runs after success or failure, including a navigation timeout:
async function capturePage(browser, url) {
const page = await browser.newPage();
try {
await page.goto(url, {
waitUntil: 'domcontentloaded',
timeout: 20_000,
});
return await page.screenshot({ type: 'png' });
} finally {
await page.close();
}
}
In a production service, consider making cleanup resilient to close errors so one failed close does not hide the original job error. Log both the job failure and whether cleanup succeeded. Do not return a live Page or ElementHandle from a job that is supposed to have ended.
#1 Best Overall
Use a browser context when jobs need isolation
A context separates a job’s cookies and page state from other jobs while sharing the browser process. Close the context after the job; closing it also closes its pages. This is useful when a job creates multiple pages or needs session isolation. Do not close a shared browser from an individual job if other work still depends on it.
async function runIsolatedJob(browser, url) {
const context = await browser.createBrowserContext();
try {
const page = await context.newPage();
await page.goto(url, {
waitUntil: 'domcontentloaded',
timeout: 20_000,
});
return await page.title();
} finally {
await context.close();
}
}
API names can differ across older Puppeteer versions; check the API for the version you have installed if a context method is unavailable.
Close the browser only at its ownership boundary
If your process launched the browser, its shutdown and fatal-error paths should call browser.close(). That closes the browser process and its pages. If Puppeteer attached to a browser owned by another service, browser.disconnect() only detaches Puppeteer; it leaves the browser and pages running. It is not a substitute for cleanup when your process owns the browser.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteFor a shared browser, pair bounded job concurrency with per-job cleanup. Recycle the browser process when measurements show retained memory continuing to rise, rather than restarting it after an arbitrary number of jobs.
Rank #2
Clean up other job-owned resources
- Remove event listeners that the job registered, especially listeners whose closures retain pages or large objects.
- Detach CDP sessions created for the job and stop streams or other ongoing work.
- If request interception was enabled, turn it off when it is no longer needed.
- Do not retain pages, element handles, response bodies, screenshots, or large result arrays in module-level caches.
- Record a job ID, browser/page identifiers, and cleanup result so an unreleased resource can be tied to its owner.
Classify the timeout before changing it
Timeouts can arise in different layers. Record the operation that was pending and set a finite limit appropriate to that operation. Puppeteer’s current LaunchOptions documentation gives browser launch a default timeout of 30,000 milliseconds; it supports an AbortSignal, and setting the launch timeout to 0 disables that timeout. Avoid disabling limits in production: a hung launch or operation can keep a worker and its resources occupied indefinitely.
| Timeout layer | What to check | Useful response |
|---|---|---|
| Browser launch | Executable path, permissions, browser cache, installed dependencies, and browser compatibility. | Fix the environment first. Set a finite launch timeout and pass an abort signal when the launch needs cancellation. |
| Navigation | Whether the URL is reachable and whether the chosen wait condition matches the task. | Use a bounded navigation timeout. Prefer DOM readiness when that is enough; a network-idle condition can wait indefinitely on pages with persistent connections. |
| Selector or action | Whether the selector exists in this page state and whether the action is waiting for the right outcome. | Use a finite default action timeout and override it only for operations known to take longer. Include the selector and operation in the error context. |
| DevTools Protocol | Whether a protocol call remains pending and what async operation initiated it. | Inspect pending protocol callbacks and their initiating stacks; investigate the underlying operation rather than raising unrelated navigation limits. |
Choose navigation readiness for the result you need
domcontentloaded can be appropriate when the task only needs the document structure. Use a stronger readiness condition when the page must finish loading a specific resource or application state. A full network-idle wait is not a universal definition of readiness: analytics, event streams, polling, and other long-lived connections can prevent it from completing.
For a click that triggers navigation, install the navigation wait before clicking. Otherwise, a fast navigation may start before Puppeteer begins waiting for it:
Recommended Free Tools
await Promise.all([
page.waitForNavigation({
waitUntil: 'domcontentloaded',
timeout: 20_000,
}),
page.click('a.next'),
]);
This coordination pattern is documented in Puppeteer’s Page API. Set the timeout to suit the operation and make sure the selector reflects the page being tested.
Use finite limits and cancel work deliberately
A larger limit is justified when an operation is expected to take longer and the service can afford to wait. It is not a general repair. Long limits can turn a quick failure into a stalled worker, raise resource occupancy, and delay cleanup. Keep limits finite, abort hung work where supported, and ensure cancellation still reaches the cleanup path.
Prove memory is leaking instead of guessing
Chrome for Developers distinguishes a memory leak—progressive growth—from memory bloat and frequent garbage collection. It does not give a universal memory ceiling: acceptable usage varies by device and browser. Compare a repeatable workload against its own baseline rather than treating one large heap reading as proof.
- Capture a baseline. Use Chrome DevTools to take a heap snapshot before the workload.
- Repeat the same work. Run the same Puppeteer job many times under comparable conditions. Keep concurrency and input stable so a growing result set does not masquerade as a leak.
- Capture again. Take another heap snapshot after the workload and allow the snapshot workflow to perform garbage collection.
- Compare retained objects. Use the Comparison view to find object counts or retained size that rise and do not fall. Look for retaining paths to globals, closures, listeners, or caches.
- Inspect detached nodes. Filter for
Detached. A detached DOM node is no longer in the document but remains reachable through JavaScript; inspect what still references it. - Trace new allocations if needed. Use Allocation Timeline when you need to find where new allocations begin.
Heap snapshots show reachable JavaScript objects and provide Summary, Comparison, Containment, and Statistics views. Puppeteer’s Page API also exposes captureHeapSnapshot() to write a page heap snapshot to a file. A page snapshot helps inspect that page’s heap; it does not replace checking process-level memory or other pages when the suspected problem is broader.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Make timeouts and leaks observable
When the failing layer is unclear, add diagnostics before increasing limits. Include the URL, operation name, timeout class, elapsed time, browser/page ID, and cleanup result in structured logs. Avoid logging secrets from URLs, headers, cookies, or page content.
Rank #4
- Run Node.js with the inspector and place a
debuggerstatement around the operation that stalls. - Set
NODE_DEBUG="puppeteer:*"to log DevTools Protocol traffic. Protect these logs because they may contain sensitive information. - Print
browser.debugInfo.pendingProtocolErrorsto inspect unresolved protocol callbacks and their initiating stacks. - Launch with
dumpio: trueto forward Chromium logs to the Node.js process.
Use these diagnostics selectively in production: protocol and browser logs can be verbose and sensitive. Pair them with browser and page identifiers, so the log line that reveals a failure can be connected to the job that must clean up.
Check the environment before tuning timeouts
Puppeteer’s troubleshooting guidance identifies several non-code causes of launch and timeout failures: a broken browser cache or executable path, missing Linux dependencies, user-permission problems, and Chromium compatibility on Alpine. Its Alpine guidance specifically warns that the current Chromium package in Alpine 3.20 can cause timeout issues and describes a compatible version or an Alpine 3.19 workaround. Treat that as version-specific advice, not a permanent fix: pin and test the Puppeteer, Chromium, and container combination you actually deploy, then retest after upgrades.
- Confirm that the configured executable exists and that the service account can run it.
- Check the installed browser dependencies in the same container or host environment where the timeout occurs.
- Compare local and production browser versions and launch configuration.
- Reproduce the issue with a small workload before changing global timeout policy.
Design production workers for recovery
A reliable browser worker makes ownership, limits, and recovery explicit. Use this checklist when reviewing an implementation:
- Isolation: decide whether each job needs a page, a separate browser context, or a separate browser process.
- Cleanup guarantee: cover success, thrown errors, timeouts, cancellation, and process shutdown with cleanup paths.
- Concurrency: cap active jobs and apply back-pressure instead of creating unbounded pages.
- Timeout policy: give launch, navigation, selectors, and actions their own finite limits.
- Diagnostics: retain enough heap, allocation, protocol, and browser-log evidence to identify the growing resource.
- Reproducibility: pin Puppeteer and Chromium versions and test the container dependencies they need.
- Recovery: decide when to recycle a browser and whether a retry could repeat side effects such as submitting a form or placing an order.
Set recycling thresholds from observed workload baselines and service objectives; the official guidance does not establish a universal leak rate, memory ceiling, or timeout value. If retries are enabled, distinguish a safe read-only retry from an action that could happen twice.
Best Value
- Used Book in Good Condition
Or skip the browser setup
If the job is simply to capture a website screenshot, a screenshot API can avoid operating your own Chromium worker for that task. ScreenshotNeo is a website screenshot API and MCP server for developers. Its one-request endpoint returns an image or PDF, and its clean-shot options accept cookie or consent banners as a visitor and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture. Each cleanup step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; responses report the page verdict and billing status in X-Page-Verdict and X-Billed headers. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
For example, this cURL request captures a page as WebP:
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 setup and options. If you need to use an HTTP client in a script instead, these examples make the same request:
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)
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}`);
The API also offers full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets and custom viewports, retina scale, PDF settings, HTML/CSS-to-image, custom CSS and JavaScript, click-before-capture, selector hiding, wait conditions, request blocking, custom headers and cookies, timezone and geolocation, transparent backgrounds, resizing, configurable caching, signed image links, async jobs with signed webhooks, bulk capture of 100 URLs per call, usage API, and OpenAPI spec. Parameter names used by other screenshot APIs also work, which can make switching easier. It is not a replacement for Puppeteer when a workflow needs general browser interaction or application-specific automation.
All ScreenshotNeo features are available on every plan: Free includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots, and yearly billing gives two months free. Sign up for ScreenshotNeo to try 1,000 free screenshots a month with no card.
Frequently Asked Questions
Does a high memory reading by itself prove a Puppeteer leak?
No. Compare repeated, equivalent workloads and inspect retained objects after garbage collection; one heap or process reading alone cannot show progressive retention.
Is it safe to set Puppeteer timeouts to zero?
It disables the applicable timeout, which can leave a worker waiting indefinitely. Keep production limits finite and cancel work that exceeds them.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsQuick Recap
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.

