Puppeteer: Node is either not visible or not an HTMLElement
Last verified
Error: Node is either not visible or not an HTMLElementWhat it means
Puppeteer needed the element's on-screen box and could not get a non-empty one. In current versions this is raised by elementHandle.screenshot() when the element has no visible bounding box. In older versions (roughly before v19) it was also the error thrown by click(), hover() and tap(), which is why so many older answers attach it to clicking; current versions report clicks as Node is either not clickable or not an Element.
Not visible here has a concrete meaning: the element has zero width or height, is display: none, sits in a collapsed parent, or is not rendered at all. "Not an HTMLElement" covers nodes with no layout box of their own, such as text nodes, some SVG children and <option> elements inside a closed select.
What is not wrong: the selector found something. It found a node with nothing to measure.
What it looks like
await page.setViewport({ width: 1366, height: 900 });
const chart = await page.waitForSelector('#sales-chart', { visible: true });
await chart.screenshot({ path: 'chart.png' });
// Choosing from a <select> without clicking hidden <option>s
await page.select('select#country', 'US');Why it happens
- The element is hidden (display:none, zero size) or inside a closed dropdown, tab or accordion.
- The selector matches a hidden duplicate, such as a mobile menu copy, before the visible one.
- Screenshotting an element before lazy-loaded content or images give it a size.
- Targeting an option element or a non-rendered node instead of its visible container.
- A small viewport hides the element via responsive CSS.
How to fix it
- Wait for visibility first: await page.waitForSelector(selector, { visible: true }).
- Make the selector specific to the visible element, or use the ::-p-aria() or ::-p-text() selectors that target rendered content.
- Perform the step that reveals the element (open the menu, switch the tab) before interacting or screenshotting.
- For select boxes, use page.select(selectSelector, value) instead of clicking options.
- Set a desktop-sized viewport with page.setViewport({ width: 1366, height: 900 }).
How to stop it recurring
Always combine element interactions and element screenshots with a visible: true wait or the Locator API, which checks visibility automatically. Use a realistic desktop viewport for scraping and screenshot jobs, and avoid selectors that can match off-screen duplicates. When upgrading from an old Puppeteer release, update error handling that matched on this string.
Official documentation: pptr.dev/api/puppeteer.elementhandle.screenshot. 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 "Node is either not visible or not an HTMLElement" mean in Puppeteer?
Puppeteer needed the element's on-screen box and could not get a non-empty one. In current versions this is raised by elementHandle.screenshot() when the element has no visible bounding box.
How do I fix "Node is either not visible or not an HTMLElement"?
1. Wait for visibility first: await page.waitForSelector(selector, { visible: true }). 2. Make the selector specific to the visible element, or use the ::-p-aria() or ::-p-text() selectors that target rendered content. 3. Perform the step that reveals the element (open the menu, switch the tab) before interacting or screenshotting. 4. For select boxes, use page.select(selectSelector, value) instead of clicking options. 5. Set a desktop-sized viewport with page.setViewport({ width: 1366, height: 900 }).
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.