When automating browser testing with Puppeteer's headless Chrome mode, you may encounter frustrating "Evaluation failed" errors. These errors commonly occur when trying to evaluate JavaScript within the headless browser context.
Here are the most common causes and fixes for "Evaluation failed" errors:
The Page Hasn't Fully Loaded
The most common reason for evaluation failures is trying to evaluate a selector before the page has fully loaded. For example:
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
// This may fail because the page hasn't finished loading
const title = await page.evaluate(() => document.querySelector('h1').textContent);
The fix is to wait for the
await page.goto('https://example.com');
// Wait for load before evaluating
await page.waitForNavigation({ waitUntil: 'load' });
// Evaluation will now succeed
const title = await page.evaluate(() => document.querySelector('h1').textContent);
This gives time for the page to fully render before attempting to query the DOM.
Complex Client-Side JavaScript
Modern web apps rely heavily on JavaScript to render content dynamically. If the website uses complex frameworks like React or Angular, it may require extra time to initialize and execute internal scripts before the page is ready for evaluation.
The solution is to wait for additional client-side events like
await page.goto('https://example.com');
// Wait for JavaScript framework to "settle" after load
await page.waitForNavigation({waitUntil: 'networkidle0'});
// Evaluations should now work
const title = await page.evaluate(...);
Be generous with wait times for single page apps or pages with lots of async JavaScript.
Accessing Shadow DOM Elements
Modern browsers implement "shadow" DOMs that hide certain DOM subtrees from standard traversal methods. You may not have access to query shadow DOM elements from
// Won't work for shadow DOM elements
const info = await page.evaluate(() => document.querySelector('.info').textContent);
To access shadow DOM, use
const element = await page.evaluateHandle(() =>
document.querySelector('.info').shadowRoot
);
const info = await page.evaluate(el => el.textContent, element);
This fetches the shadow root that you can pass back into the browser context for further evaluation.
Waiting for Elements to Exist
Sometimes you need to wait for asynchronous DOM updates to make an element available before you can evaluate it:
// This will fail if .updated doesn't exist yet
await page.evaluate(() => document.querySelector('.updated').textContent);
To avoid timing issues, use browser waits:
// Wait up to 10 seconds for element to be present
await page.waitForSelector('.updated', {timeout: 10000});
// We can now safely evaluate the element
const text = await page.evaluate(...);
This pauses Puppeteer until the element is available in the DOM before continuing.
Summary
In summary, avoid evaluation errors by:
- Waiting for
load andDOMContentLoaded events - Accounting for complex client-side JavaScript
- Accessing shadow DOM with
page.evaluateHandle() - Adding waits before evaluating elements
With proper synchronization, you can reliably automate headless Chrome using Puppeteer's