Puppeteer: TimeoutError: Waiting for selector failed
Last verified
TimeoutError: Waiting for selector `.results` failed
[cause]: TimeoutError: Waiting failed: 30000ms exceededWhat it means
page.waitForSelector() (or frame.waitForSelector()) polled for the selector for its whole timeout, 30 seconds by default, and the condition was never met. In current Puppeteer the timeout detail is attached as the error's cause; older releases printed it on the same line, for example Waiting for selector `.results` failed: Waiting failed: 30000ms exceeded.
By default the method only waits for the element to exist in the DOM. With { visible: true } it also requires a non-empty bounding box and no visibility: hidden; with { hidden: true } it waits for the opposite. So the same selector can succeed or time out depending on those options.
What is not wrong: the timeout length is rarely the problem. The selector either does not match what the page renders, is looked for in the wrong frame, or matches an element that never becomes visible.
What it looks like
try {
await page.waitForSelector('.results', { visible: true, timeout: 15000 });
} catch (err) {
await page.screenshot({ path: 'failure.png', fullPage: true });
console.error(err.message, err.cause?.message);
throw err;
}
// Locator API (Puppeteer 21+): waits, then clicks
await page.locator('button.load-more').click();Why it happens
- The selector does not match the rendered markup: generated class names, changed IDs, or text that differs from what you expected.
- The element is inside an iframe or a shadow root that page-level CSS selectors do not reach.
- visible: true was requested but the element stays hidden until another interaction.
- The site served a different page (consent wall, login redirect, bot challenge, empty search results).
- The content loads from an API call that failed or was blocked by request interception.
How to fix it
- Take a screenshot and dump page.content() at the moment of failure to see what was actually rendered.
- Test the selector in DevTools on the same page state with document.querySelectorAll().
- For iframes, get the frame (await (await page.$('iframe')).contentFrame()) and wait inside it.
- Use Puppeteer's shadow-piercing selector syntax (>>> or the pierce/ prefix) for web components.
- Drop visible: true if you only need the element to exist, or perform the step that reveals it.
- Consider page.locator(selector).click(), which waits for the element to be visible, enabled and stable before acting.
How to stop it recurring
Prefer stable attributes (data-testid, name, ARIA roles via ::-p-aria()) over generated class names. Capture a screenshot and HTML on every timeout so failures are diagnosable without rerunning. Handle consent walls and empty states explicitly so they produce clear errors instead of 30-second waits.
Official documentation: pptr.dev/api/puppeteer.page.waitforselector. See all Puppeteer error messages.
Related errors
Definitions
- PuppeteerPuppeteer is Google's open-source Node.js library for controlling Chrome and Firefox. It drives Chrome over th
- Chrome DevTools Protocol (CDP)The Chrome DevTools Protocol (CDP) is the JSON-over-WebSocket interface Chrome exposes for inspecting and cont
- CSS SelectorA CSS selector is a pattern used to identify and target specific HTML elements on a web page, widely used in b
- Headless ChromeHeadless Chrome is Google Chrome running without a visible window. It renders pages and runs JavaScript like n
- IframeAn iframe (inline frame) is an HTML element that embeds a separate web page within a parent page, creating an
- Web ElementA web element is any individual component rendered on a web page — buttons, links, input fields, images, text
Quick answers
What does "TimeoutError: Waiting for selector failed" mean in Puppeteer?
page.waitForSelector() (or frame.waitForSelector() ) polled for the selector for its whole timeout, 30 seconds by default, and the condition was never met. In current Puppeteer the timeout detail is attached as the error's cause ; older releases printed it on the same line, for example Waiting for selector `.results` failed: Waiting failed: 30000ms exceeded .
How do I fix "TimeoutError: Waiting for selector failed"?
1. Take a screenshot and dump page.content() at the moment of failure to see what was actually rendered. 2. Test the selector in DevTools on the same page state with document.querySelectorAll(). 3. For iframes, get the frame (await (await page.$('iframe')).contentFrame()) and wait inside it. 4. Use Puppeteer's shadow-piercing selector syntax (>>> or the pierce/ prefix) for web components. 5. Drop visible: true if you only need the element to exist, or perform the step that reveals it. 6. Consider page.locator(selector).click(), which waits for the element to be visible, enabled and stable…
Run browser automation you can watch and take over
Autonoly runs each job in a real cloud Chromium browser driven by Playwright. Timeouts and detached elements still happen; the difference is that you can watch the run live, see the step that failed, and take control of the browser to finish it.
Also see Live browser control.