Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run Scan×
Skip to content
browser automation

How to Fix Puppeteer Pages That Never Finish Loading

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

If a Puppeteer page never finishes loading, first identify which promise is still pending. A stalled page.goto(), waitForNavigation(), waitForNetworkIdle(), locator wait, or request-interception handler represents a different condition and needs a different fix. Start by checking request interception, then verify navigation sequencing, choose a wait that matches your actual milestone, and keep every wait bounded by a finite timeout.

Find the operation that is actually waiting

There is no single Puppeteer signal called “page complete.” Navigation, network-idle, element visibility and application state are separate milestones. Log immediately before and after each potentially pending call so you can identify the unresolved operation.

console.log('before goto');
await page.goto('https://example.com');
console.log('after goto');

Repeat this around waitForNavigation(), waitForNetworkIdle(), locator waits and any asynchronous code inside a request event handler. Note whether the call eventually throws a timeout or remains pending indefinitely. A timeout identifies the condition that was not reached; it does not, by itself, identify why.

Fix request interception first

If your code calls page.setRequestInterception(true), every intercepted request pauses until Puppeteer resolves it. The official guidance is direct: “Puppeteer requires request.continue() to be called explicitly or the request will hang.” Every branch must call exactly one of continue(), respond() or abort(), unless the browser completes the request from cache.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
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

Allow requests by default

await page.setRequestInterception(true);

page.on('request', request => {
  if (request.resourceType() === 'image') {
    return request.abort();
  }

  // The default branch must resolve the request.
  return request.continue();
});

A common hang is an early return that does nothing:

// Bug: this leaves matching requests unresolved.
page.on('request', request => {
  if (request.url().includes('/analytics')) return;
  request.continue();
});

Resolve the branch explicitly instead:

page.on('request', request => {
  if (request.url().includes('/analytics')) {
    return request.abort();
  }
  return request.continue();
});

Guard against multiple handlers

Another listener, test helper or third-party package may resolve the same interception first. Check request.isInterceptResolutionHandled() immediately before resolving. In an asynchronous handler, check again after every await, because another listener can act while your handler is suspended.

page.on('request', async request => {
  if (request.isInterceptResolutionHandled()) return;

  const shouldBlock = request.url().includes('tracker');
  await Promise.resolve(); // asynchronous policy lookup, for example

  if (request.isInterceptResolutionHandled()) return;
  if (shouldBlock) {
    await request.abort();
  } else {
    await request.continue();
  }
});

Keep the final check and the resolution call together. If you have several listeners, temporarily disable interception or remove listeners to confirm whether the handler is the source of the stall.

Rank #2
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

Pair a navigation wait with the action that triggers it

When a click submits a form or follows a link, arm the navigation wait and perform the action in the same Promise.all. Starting the click first can allow the navigation event to occur before waitForNavigation() is listening.

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('navigation response:', response ? response.status() : 'null');

waitForNavigation() watches for a new URL or a reload and returns the main-resource response. Anchor changes and History API navigation can resolve with null; that is a completed navigation wait, not an indication that the promise is still stuck.

Use the same pattern for form submission

const [response] = await Promise.all([
  page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
  page.click('button[type="submit"]'),
]);

Choose navigation options that match the application. If the button updates the current document through fetch or the History API rather than loading a new document, a navigation wait may be the wrong condition. Wait for the resulting UI state instead.

Choose a wait condition that matches the work

Wait strategy What it represents Use it when Main caveat
waitForNavigation() A new URL or reload; returns the main-resource response, or null for anchor/History API cases An action is expected to navigate Pair it with the action using Promise.all to avoid a race
waitForNetworkIdle() Network activity satisfies the configured idle condition The next operation genuinely requires network quiet Long-polling, analytics or streaming can prevent idle; readiness may occur earlier
Locator or function wait A specific element or application condition is satisfied The next step needs a known UI state The selector or predicate must describe the real milestone
Navigation timeout A maximum duration for navigation-related calls Every navigation should fail within a known bound It bounds waiting but does not repair a slow or unresolved operation

Network idle is not universal readiness

waitForNetworkIdle() is useful for pages that become stable after requests settle, but it is a poor fit for applications that poll continuously or keep a socket open. The current options reference lists concurrency defaulting to 0 and idleTime defaulting to 500 milliseconds; verify these defaults against the Puppeteer version installed in your project.

await page.waitForNetworkIdle({
  concurrency: 0,
  idleTime: 500,
  timeout: 15000,
});

If your task is “click the Export button once it is visible,” wait for that button rather than for every request to stop.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const exportButton = page.getByRole('button', { name: 'Export' });
await exportButton.wait({ state: 'visible', timeout: 10000 });
await exportButton.click();

A lower-level alternative is:

await page.waitForSelector('[data-testid="export-ready"]', {
  visible: true,
  timeout: 10000,
});

For state that has no single element, use a function-based condition:

await page.waitForFunction(
  () => document.body.dataset.status === 'ready',
  { timeout: 10000 }
);

Set finite timeouts and capture evidence

Set a navigation timeout so a missing milestone becomes an actionable error instead of an indefinitely pending test.

page.setDefaultNavigationTimeout(30000);

try {
  await page.goto('https://example.com', {
    waitUntil: 'domcontentloaded',
  });
} catch (error) {
  console.error('Navigation failed:', error);
}

This setting affects navigation-related methods including goto, reload, setContent and waitForNavigation. Raising the number only changes the bound; disabling timeouts can hide the original diagnosis. Use a longer value only when you have evidence that the target legitimately needs more time.

Log requests while reproducing

page.on('request', request => {
  console.log('request', request.method(), request.url());
});
page.on('requestfailed', request => {
  console.warn('failed', request.url(), request.failure());
});
page.on('response', response => {
  if (response.status() >= 400) {
    console.warn('HTTP', response.status(), response.url());
  }
});

Compare the last logged request with the pending promise. A request that never receives a resolution points to interception; a completed document with a missing selector points to the selector or application state; repeated requests point to a network-idle mismatch.

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.
Best Value
Sale
JavaScript and jQuery: Interactive Front-End Web Development
  • JavaScript Jquery
  • Introduces core programming concepts in JavaScript and jQuery
  • Uses clear descriptions, inspiring examples, and easy-to-follow diagrams
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Common symptoms and targeted fixes

page.goto() times out

  • Check whether interception is enabled and every handler branch resolves.
  • Try waitUntil: 'domcontentloaded' when waiting for all subresources is unnecessary.
  • Inspect failed requests, redirects and authentication requirements.
  • Keep the timeout finite and investigate the specific request or lifecycle event that did not complete.

waitForNavigation() never resolves after a click

  • Use Promise.all with the wait created before or alongside the click.
  • Confirm that the click really causes a document navigation; single-page apps may only change history or render new content.
  • If it is an anchor or History API transition, expect a null response and wait for the destination UI.

waitForNetworkIdle() times out

  • Look for polling, analytics, advertisements, WebSockets or streaming requests.
  • Replace network idle with a locator or function wait for the state your script needs.
  • If network quiet is essential, configure an appropriate concurrency and idle period and retain a finite timeout.

The page appears loaded but the next action fails

  • Wait for the exact element to be visible or enabled.
  • Wait for an application state attribute or a function predicate.
  • Check that the selector describes the post-load state rather than a placeholder rendered before data arrives.

Or skip the browser setup

If your goal is a clean image or PDF rather than browser automation, ScreenshotNeo provides a GET endpoint that captures a URL as PNG, JPEG, WebP or PDF. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. It also offers an MCP server with take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients.

cURL

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

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}`);

See the full option list and request details in the ScreenshotNeo documentation. Every plan includes its features: full-page and element capture, device presets, custom CSS and JavaScript, waits, request blocking, cookies and headers, PDFs, caching, signed links, asynchronous jobs, bulk capture and a usage API. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Version and diagnosis caveats

Puppeteer APIs and defaults can change. The reference surfaced for this guidance was Puppeteer 25.12.0, while the navigation-timeout reference used a “next” documentation path. Check the API reference for the exact version in your package before copying version-sensitive options. Without the target URL, source code, logs and request trace, no single cause can be confirmed for an individual run.

Frequently Asked Questions

Does a successful HTTP response prove that Puppeteer is finished?

No. The response covers the navigation resource, while scripts may still render content, poll APIs or wait for client-side state. Wait for the milestone your next operation requires.

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

Why can a navigation response be null?

Anchor changes and History API navigation can complete without a new main-resource response, so Puppeteer documents a null result for those cases.

Should I disable Puppeteer timeouts while debugging?

No. A finite timeout exposes the pending condition. Disabling it can leave a broken wait unresolved and remove the error that would help locate the cause.

Quick Recap

SaleBestseller No. 1
HTML and CSS: Design and Build Websites
HTML and CSS: Design and Build Websites
HTML CSS Design and Build Web Sites; Comes with secure packaging; It can be a gift option
$14.18
SaleBestseller No. 2
Web Design with HTML, CSS, JavaScript and jQuery Set
Web Design with HTML, CSS, JavaScript and jQuery Set
Brand: Wiley; Set of 2 Volumes
$35.05
SaleBestseller No. 3
SaleBestseller No. 5
JavaScript and jQuery: Interactive Front-End Web Development
JavaScript and jQuery: Interactive Front-End Web Development
JavaScript Jquery; Introduces core programming concepts in JavaScript and jQuery; Uses clear descriptions, inspiring examples, and easy-to-follow diagrams
$22.80

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
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

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.