How to handle errors in Puppeteer?

Error handling is essential for building robust Puppeteer applications. This guide covers all major error types and handling strategies you'll encounter when automating browsers.

Basic Error Handling with Try/Catch

All Puppeteer operations are asynchronous and return promises. Use try/catch blocks to handle errors gracefully:

const puppeteer = require('puppeteer');

(async () => {
  let browser;
  try {
    browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('https://example.com');
    await page.screenshot({path: 'example.png'});
  } catch (error) {
    console.error('Error occurred:', error.message);
    console.error('Stack trace:', error.stack);
  } finally {
    // Always close browser to prevent resource leaks
    if (browser) await browser.close();
  }
})();

Handling Timeout Errors

Timeout errors are common in web automation. Handle them by adjusting timeouts or implementing retry logic:

const puppeteer = require('puppeteer');

(async () => {
  let browser;
  try {
    browser = await puppeteer.launch();
    const page = await browser.newPage();

    // Configure timeouts
    page.setDefaultNavigationTimeout(30000); // 30 seconds
    page.setDefaultTimeout(10000); // 10 seconds for other operations

    await page.goto('https://slow-loading-site.com');

    // Wait for specific element with custom timeout
    await page.waitForSelector('#content', { timeout: 5000 });

  } catch (error) {
    if (error.name === 'TimeoutError') {
      console.error('Operation timed out:', error.message);
      // Implement retry logic here
    } else {
      console.error('Other error:', error.message);
    }
  } finally {
    if (browser) await browser.close();
  }
})();

Network Error Handling

Monitor and handle network failures, failed requests, and connection issues:

const puppeteer = require('puppeteer');

(async () => {
  let browser;
  try {
    browser = await puppeteer.launch();
    const page = await browser.newPage();

    // Track failed requests
    const failedRequests = [];
    page.on('requestfailed', request => {
      failedRequests.push({
        url: request.url(),
        errorText: request.failure().errorText
      });
      console.error(`Request failed: ${request.url()} - ${request.failure().errorText}`);
    });

    // Handle page errors
    page.on('pageerror', error => {
      console.error('Page error:', error.message);
    });

    // Handle console errors from the page
    page.on('console', msg => {
      if (msg.type() === 'error') {
        console.error('Browser console error:', msg.text());
      }
    });

    await page.goto('https://example.com');

    // Check if critical requests failed
    if (failedRequests.length > 0) {
      console.warn(`${failedRequests.length} requests failed`);
    }

  } finally {
    if (browser) await browser.close();
  }
})();

Browser Crash Handling

Handle browser crashes and disconnections gracefully:

const puppeteer = require('puppeteer');

(async () => {
  let browser;
  try {
    browser = await puppeteer.launch();

    // Handle browser disconnection
    browser.on('disconnected', () => {
      console.error('Browser disconnected unexpectedly');
    });

    const page = await browser.newPage();
    await page.goto('https://example.com');

  } catch (error) {
    if (error.message.includes('Target closed') || error.message.includes('Protocol error')) {
      console.error('Browser crashed or was closed:', error.message);
    } else {
      console.error('Unexpected error:', error.message);
    }
  } finally {
    try {
      if (browser && browser.isConnected()) {
        await browser.close();
      }
    } catch (closeError) {
      console.error('Error closing browser:', closeError.message);
    }
  }
})();

Retry Logic Implementation

Implement robust retry mechanisms for handling transient failures:

const puppeteer = require('puppeteer');

async function retryOperation(operation, maxRetries = 3, delay = 1000) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await operation();
    } catch (error) {
      console.error(`Attempt ${attempt} failed:`, error.message);

      if (attempt === maxRetries) {
        throw new Error(`Operation failed after ${maxRetries} attempts: ${error.message}`);
      }

      // Wait before retrying
      await new Promise(resolve => setTimeout(resolve, delay * attempt));
    }
  }
}

(async () => {
  let browser;
  try {
    browser = await puppeteer.launch();

    await retryOperation(async () => {
      const page = await browser.newPage();
      await page.goto('https://unreliable-site.com');
      const title = await page.title();
      console.log('Page title:', title);
      await page.close();
    });

  } catch (error) {
    console.error('All retry attempts failed:', error.message);
  } finally {
    if (browser) await browser.close();
  }
})();

Element Interaction Error Handling

Handle errors when interacting with page elements:

const puppeteer = require('puppeteer');

(async () => {
  let browser;
  try {
    browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('https://example.com');

    // Safe element interaction
    try {
      await page.waitForSelector('#submit-button', { timeout: 5000 });
      await page.click('#submit-button');
    } catch (error) {
      if (error.name === 'TimeoutError') {
        console.error('Submit button not found within timeout');
        // Try alternative selector or action
        const alternativeButton = await page.$('#alt-submit');
        if (alternativeButton) {
          await alternativeButton.click();
        }
      } else {
        throw error; // Re-throw unexpected errors
      }
    }

    // Safe text extraction with fallback
    const getText = async (selector, fallback = 'N/A') => {
      try {
        return await page.$eval(selector, el => el.textContent.trim());
      } catch (error) {
        console.warn(`Could not extract text from ${selector}:`, error.message);
        return fallback;
      }
    };

    const title = await getText('h1', 'No title found');
    console.log('Page title:', title);

  } finally {
    if (browser) await browser.close();
  }
})();

Comprehensive Error Handling Pattern

Here's a complete example combining all error handling strategies:

const puppeteer = require('puppeteer');

class PuppeteerErrorHandler {
  constructor(options = {}) {
    this.maxRetries = options.maxRetries || 3;
    this.timeout = options.timeout || 30000;
    this.retryDelay = options.retryDelay || 1000;
  }

  async withErrorHandling(operation) {
    let browser;
    let attempt = 0;

    while (attempt < this.maxRetries) {
      try {
        browser = await puppeteer.launch({
          headless: 'new',
          args: ['--no-sandbox', '--disable-setuid-sandbox']
        });

        browser.on('disconnected', () => {
          console.warn('Browser disconnected');
        });

        const result = await operation(browser);
        return result;

      } catch (error) {
        attempt++;
        console.error(`Attempt ${attempt} failed:`, error.message);

        if (this.isRetryableError(error) && attempt < this.maxRetries) {
          console.log(`Retrying in ${this.retryDelay * attempt}ms...`);
          await this.delay(this.retryDelay * attempt);
        } else {
          throw error;
        }
      } finally {
        if (browser) {
          try {
            await browser.close();
          } catch (closeError) {
            console.error('Error closing browser:', closeError.message);
          }
        }
      }
    }
  }

  isRetryableError(error) {
    const retryableErrors = [
      'TimeoutError',
      'net::ERR_NETWORK_CHANGED',
      'net::ERR_CONNECTION_RESET',
      'net::ERR_INTERNET_DISCONNECTED'
    ];

    return retryableErrors.some(errType => 
      error.name === errType || error.message.includes(errType)
    );
  }

  delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// Usage example
(async () => {
  const errorHandler = new PuppeteerErrorHandler({
    maxRetries: 3,
    timeout: 30000,
    retryDelay: 2000
  });

  try {
    const result = await errorHandler.withErrorHandling(async (browser) => {
      const page = await browser.newPage();
      page.setDefaultTimeout(30000);

      await page.goto('https://example.com');
      const title = await page.title();

      return { title, success: true };
    });

    console.log('Operation successful:', result);
  } catch (error) {
    console.error('Final error after all retries:', error.message);
  }
})();

Best Practices Summary

  1. Always use try/catch blocks around Puppeteer operations
  2. Implement proper cleanup in finally blocks to prevent resource leaks
  3. Set appropriate timeouts for different operations
  4. Monitor browser events for disconnections and crashes
  5. Implement retry logic for transient failures
  6. Log errors appropriately for debugging and monitoring
  7. Handle element interaction failures with fallback strategies
  8. Use specific error types to determine retry vs. fail-fast behavior

By implementing comprehensive error handling, your Puppeteer applications will be more reliable and maintainable in production environments.

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon