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

If page.waitForSelector() sits for 30 seconds and then throws, Puppeteer has reached its documented default timeout: 30,000 milliseconds. The timeout does not necessarily mean Puppeteer is broken; it means the selector did not meet the requested conditions in the page context being checked. Confirm the DOM, visibility, frame or shadow-root context, and navigation timing before increasing the wait. Use timeout: 0 only when an unbounded wait is genuinely acceptable.

What the 30-second wait means

Puppeteer’s API describes page.waitForSelector() as waiting for a selector to appear in the page. If it does not appear before the timeout, the function throws. The documented default is 30000 milliseconds; timeout: 0 disables that timeout. The Page API also provides page.setDefaultTimeout() to change the default for applicable waits. See the waitForSelector API reference and setDefaultTimeout API reference.

A timeout is a report about a wait condition, not proof that the browser needs more time. The element may never be inserted, may be hidden when visibility was required, may live in another frame or shadow root, or the script may be waiting through a navigation race. Start by identifying which condition is false.

Make the failure quick and observable

While diagnosing, use a short explicit timeout and capture what Puppeteer sees immediately before waiting. This makes a missing element easier to distinguish from a slow page and avoids repeatedly spending 30 seconds on an uninformative failure.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Elebase USB to USB C Adapter for iPhone 18 Pro Max,USBC Car Charger Adapter
  • Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
  • Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
  • Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
  • Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
  • 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.
const selector = '#result';
console.log('URL before wait:', page.url());
console.log((await page.content()).slice(0, 4000));
await page.screenshot({ path: 'before-wait.png', fullPage: true });

const element = await page.waitForSelector(selector, {
  visible: true,
  timeout: 5000,
});

Use the full HTML or a targeted inspection in real debugging rather than relying only on a truncated log. The screenshot helps reveal overlays, incomplete rendering, and layout differences. If the call throws, retain the exact exception text and note which options were passed; these establish whether the wait was for presence alone or for visibility as well.

Check the selector and visibility separately

First test whether the node exists

Run the wait without visible: true. If it succeeds without that option but fails with it, the selector is finding a node, but the node is not visible according to Puppeteer’s visibility check. The API’s visible option waits for an element to be present and visible; the hidden option waits for it to be hidden or detached. Consult the waitForSelector options.

const present = await page.waitForSelector('#result', { timeout: 5000 });
const visible = await page.waitForSelector('#result', {
  visible: true,
  timeout: 5000,
});

Inspect the matched element’s computed style and surrounding layout if presence succeeds but visibility does not. Check for display: none, visibility: hidden, zero-size layout, a collapsed container, or a covering overlay. Do not remove the visibility requirement merely to make the test pass if the next action requires a user-visible control.

Verify the selector against the rendered page

Compare the selector with the actual markup returned by await page.content(). Common mistakes include selecting a class that changes between renders, using a selector from a different page state, missing an iframe boundary, or expecting a client-rendered element before the application has inserted it. Prefer a stable test ID or semantic selector when the site provides one; otherwise verify the exact CSS selector and escaping against the current DOM.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Anker USB-C Hub, 5-in-1 USB Hub for Laptops, 4K HDMI Multiport Adapter
  • 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
  • 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
  • Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
  • 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
  • What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.

Puppeteer supports more than ordinary CSS selector queries, including text, accessibility role and name, XPath, and selector combinations that can cross shadow-root boundaries. Use the selector syntax documented for your installed version when a plain CSS query in the main document cannot reach the target. See Puppeteer page interactions and selector syntax.

Check frames and shadow roots

Look in the frame that owns the element

page.waitForSelector() checks the page’s main frame. An element inside an embedded frame belongs to that child frame’s document, so waiting on the page-level selector will not find it. Inspect the attached frames, identify the frame by URL or another reliable property, and run the selector wait on that frame.

for (const frame of page.frames()) {
  console.log('Frame URL:', frame.url());
}

const targetFrame = page.frames().find(frame => frame.url().includes('/embedded/'));
if (!targetFrame) throw new Error('Target frame not found');

await targetFrame.waitForSelector('#result', {
  visible: true,
  timeout: 5000,
});

Do not assume that an iframe is attached at the same moment as the outer page. If it is dynamically created, first wait for or locate the frame, then wait inside it. Puppeteer’s Page API documents the page and frame relationship.

Account for shadow DOM

Elements rendered under a shadow root are not always reachable with a conventional selector from the document root. Use Puppeteer’s documented selector syntax for shadow-root traversal or a supported text, role/name, or XPath selector where appropriate. A selector that works in DevTools after manually navigating into a component does not prove that the same query from the top-level document can reach it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #3
Sale
Anker USB C Hub, 7in1 Multi-Port USB Adapter, 4K@60Hz USBC to HDMI Splitter
  • Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
  • Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
  • Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
  • Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
  • What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.

Synchronize navigation before waiting for the result

If a click or form submission triggers navigation, start the navigation wait before performing the action. Await both together, then wait for the result in the new page state:

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

await page.waitForSelector('#result', {
  visible: true,
  timeout: 10000,
});

This ordering avoids a race in which the click begins navigation before the script starts listening for it. Puppeteer explicitly warns that a separate navigation wait started after a click can yield an unexpected race; see the Page API documentation. Choose the navigation lifecycle condition that matches the application: waiting for domcontentloaded does not guarantee that client-side rendering or later network activity has completed, so still wait for the application-specific result selector.

Check request interception and page activity

When request interception is enabled, each intercepted request must be continued, answered, or aborted. A request left unresolved stalls, which can prevent the page or application from reaching the state your selector requires. Temporarily disable interception during diagnosis if it is not essential; otherwise log each request and make sure every handler reaches a resolution path.

page.on('request', request => {
  console.log('Request:', request.url(), request.isInterceptResolutionHandled());
});

page.on('requestfailed', request => {
  console.log('Failed request:', request.url(), request.failure()?.errorText);
});

If interception is enabled, inspect the handlers that call continue(), respond(), or abort(), including conditional branches and error paths. The Puppeteer Page API notes that intercepted requests stall until resolved.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Sale
UGREEN USB to USB C Adapter Combo 4-Pack, 10Gbps USB C Converter Space Gray
  • Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
  • Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
  • Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
  • Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
  • Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft

Use timeouts deliberately

Set a timeout for one wait

A per-call timeout makes a particular wait’s tolerance explicit without changing unrelated waits:

await page.waitForSelector('#result', {
  visible: true,
  timeout: 10000,
});

This is appropriate when the element has a known, legitimately variable load time. Raising the number does not fix an incorrect selector, the wrong frame, a hidden element, or a navigation race; it only delays the failure in those cases.

Change the default for a page

Use page.setDefaultTimeout(milliseconds) when many waits on that page need a consistent default. Keep critical waits explicit when their timing requirements differ, so a global change does not silently make unrelated failures slow.

page.setDefaultTimeout(10000);
await page.waitForSelector('#result', { visible: true });

Disable the timeout only with an outer bound

timeout: 0 disables the selector wait’s timeout. It can be suitable when another reliable mechanism controls the overall operation, but by itself it can leave a script waiting forever if the selector never appears. In automation, a bounded wait normally provides a more diagnosable failure than an unlimited one.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
Anker USB C Hub, 5-in-1 USBC to HDMI Splitter with 4K Display
  • 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
  • Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
  • Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
  • HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
  • What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Check the installed Puppeteer version

Verify the version your script actually loads, rather than assuming it matches a globally installed package or the latest documentation:

console.log(require('puppeteer/package.json').version);

Puppeteer issue #9927, opened March 28, 2023, documents a confirmed regression in v19.8.0 where waits could still time out at 30 seconds despite higher timeout settings. That is a version-specific report, not evidence that every 30-second timeout is a Puppeteer bug. If you are on v19.8.0 and observe the reported behavior, upgrade to a currently supported release and retest; otherwise first check the selector conditions and page context.

A practical decision sequence

  1. Record the exact error, selector, options, installed Puppeteer version, and effective timeout.
  2. Log page.url(), inspect await page.content(), and save a screenshot directly before the wait.
  3. Try a short wait without a visibility requirement, then compare it with the same wait using visible: true.
  4. Verify the selector and expected page state against the current rendered DOM.
  5. Check page.frames() and wait in the child frame if that is where the element lives.
  6. Use Puppeteer’s documented selector syntax if the target is inside a shadow root or requires text/role-based selection.
  7. If an action navigates, start waitForNavigation() before the action using Promise.all.
  8. Log and resolve every intercepted request, or disable interception temporarily to isolate it.
  9. Only after those checks, adjust the per-call or page-wide timeout to fit the real load behavior.

Common errors and fixes

Symptom Likely cause What to do
It fails at 30 seconds despite the element being visible in your normal browser Puppeteer is seeing a different page state, session, viewport, or DOM than the manual browser. Capture the Puppeteer URL, HTML, and screenshot; verify login, page state, selector, and context.
The wait works without visible: true but not with it The matching node exists but is hidden, collapsed, or covered. Inspect computed styles and layout; wait for the actual visible state needed by the next step.
The selector appears in an iframe The wait is running against the main frame. Find the owning frame in page.frames() and call waitForSelector() on that frame.
The page changes after a click, but the next selector never appears The click and navigation wait may be racing, or the result is rendered after the chosen navigation event. Use the Promise.all navigation pattern, then wait for the application-specific result.
Requests or the page appear stalled A request-interception handler may leave a request unresolved. Log interception and ensure every request is continued, responded to, or aborted.
A larger timeout is ignored and failure still occurs at 30 seconds Check the loaded Puppeteer version; v19.8.0 had a confirmed report matching this symptom. Upgrade from that version to a current supported release and retest.
It waits forever after setting timeout: 0 The timeout was disabled and no external bound controls the operation. Restore a finite timeout or wrap the overall operation in an application-level deadline.

Or skip the browser setup

If the task is to capture a web page rather than automate an interaction, ScreenshotNeo provides a website screenshot API and MCP server. Its one-call API returns an image or PDF, which can avoid maintaining a local browser script for straightforward captures. See the ScreenshotNeo API documentation.

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

ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, with response headers indicating the page verdict and billing status. Its MCP server offers take_screenshot, get_page_info, and capture_pdf tools for AI agents and MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

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

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

Frequently Asked Questions

Does waitForSelector wait for an element to finish loading its content?

No. It waits for the selector condition, such as presence or requested visibility; application-specific readiness may require waiting for a separate state or result.

Can I use waitForSelector after a page.goto call?

Yes. Wait for the relevant selector after navigation, choosing a navigation condition and selector condition that match how the site renders.

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.

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