Fixing "Evaluation Failed" Errors When Using Headless Chrome in Puppeteer

Apr 2, 2024 ยท 3 min read

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 load event before evaluating:

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 domcontentloaded or networkidle0 before evaluating:

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 page.evaluate():

// Won't work for shadow DOM elements
const info = await page.evaluate(() => document.querySelector('.info').textContent); 

To access shadow DOM, use page.evaluateHandle() instead:

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:

  1. Waiting for load and DOMContentLoaded events
  2. Accounting for complex client-side JavaScript
  3. Accessing shadow DOM with page.evaluateHandle()
  4. Adding waits before evaluating elements

With proper synchronization, you can reliably automate headless Chrome using Puppeteer's page.evaluate() method without pesky errors interrupting your test flows.

Browse by language:

The easiest way to do Web Scraping

Get HTML from any page with a simple API call. We handle proxy rotation, browser identities, automatic retries, CAPTCHAs, JavaScript rendering, etc automatically for you


Try ProxiesAPI for free

curl "http://api.proxiesapi.com/?key=API_KEY&url=https://example.com"

<!doctype html>
<html>
<head>
    <title>Example Domain</title>
    <meta charset="utf-8" />
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
...

X

Don't leave just yet!

Enter your email below to claim your free API key: