Error handling in Playwright is essential for building robust web automation scripts. This guide covers common error types, handling strategies, and best practices for both Python and JavaScript.
Common Playwright Error Types
Playwright throws specific errors that you should handle differently:
- TimeoutError: Element not found within timeout period
- Error: General navigation, clicking, or interaction failures
- PlaywrightError: Browser launch or connection issues
- NetworkError: Network-related failures
Python Error Handling
Basic Exception Handling
from playwright.sync_api import sync_playwright, TimeoutError, Error
def scrape_with_error_handling():
with sync_playwright() as p:
browser = None
try:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com", timeout=30000)
# Handle element interaction errors
try:
page.click("#submit-button", timeout=5000)
print("Button clicked successfully")
except TimeoutError:
print("Button not found within timeout")
except Error as e:
print(f"Click failed: {e}")
except Exception as e:
print(f"Browser error: {e}")
finally:
if browser:
browser.close()
scrape_with_error_handling()
Specific Error Type Handling
from playwright.sync_api import sync_playwright, TimeoutError, Error
def handle_specific_errors():
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
try:
# Navigation with error handling
page.goto("https://example.com")
# Element selection with multiple fallbacks
selectors = ["#primary-button", ".submit-btn", "input[type='submit']"]
button_found = False
for selector in selectors:
try:
page.click(selector, timeout=2000)
button_found = True
break
except TimeoutError:
continue
except Error as e:
print(f"Error with selector {selector}: {e}")
continue
if not button_found:
print("No submit button found with any selector")
except TimeoutError:
print("Page load timeout")
except Error as e:
print(f"Navigation error: {e}")
finally:
browser.close()
handle_specific_errors()
Retry Logic with Exponential Backoff
import time
from playwright.sync_api import sync_playwright, TimeoutError
def retry_with_backoff(func, max_retries=3, base_delay=1):
for attempt in range(max_retries):
try:
return func()
except TimeoutError as e:
if attempt == max_retries - 1:
raise e
delay = base_delay * (2 ** attempt)
print(f"Attempt {attempt + 1} failed, retrying in {delay}s...")
time.sleep(delay)
def scrape_with_retry():
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
def navigate_and_click():
page.goto("https://example.com")
page.click("#dynamic-button")
return page.text_content("h1")
try:
result = retry_with_backoff(navigate_and_click)
print(f"Success: {result}")
except Exception as e:
print(f"All retry attempts failed: {e}")
finally:
browser.close()
JavaScript Error Handling
Basic Error Handling
const { chromium } = require('playwright');
async function scrapeWithErrorHandling() {
let browser;
try {
browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://example.com', { timeout: 30000 });
// Handle element interaction errors
try {
await page.click('#submit-button', { timeout: 5000 });
console.log('Button clicked successfully');
} catch (error) {
if (error.name === 'TimeoutError') {
console.log('Button not found within timeout');
} else {
console.log(`Click failed: ${error.message}`);
}
}
} catch (error) {
console.error(`Browser error: ${error.message}`);
} finally {
if (browser) {
await browser.close();
}
}
}
scrapeWithErrorHandling();
Advanced Error Handling with Custom Functions
const { chromium, TimeoutError } = require('playwright');
class PlaywrightErrorHandler {
static async withRetry(fn, maxRetries = 3, delay = 1000) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxRetries) throw error;
console.log(`Attempt ${attempt} failed: ${error.message}`);
await new Promise(resolve => setTimeout(resolve, delay * attempt));
}
}
}
static async safeClick(page, selector, options = {}) {
const defaultOptions = { timeout: 5000, ...options };
try {
await page.waitForSelector(selector, { timeout: defaultOptions.timeout });
await page.click(selector, defaultOptions);
return true;
} catch (error) {
console.warn(`Failed to click ${selector}: ${error.message}`);
return false;
}
}
static async safeNavigate(page, url, options = {}) {
const defaultOptions = { timeout: 30000, waitUntil: 'networkidle', ...options };
try {
await page.goto(url, defaultOptions);
return true;
} catch (error) {
console.error(`Navigation to ${url} failed: ${error.message}`);
return false;
}
}
}
async function advancedErrorHandling() {
const browser = await chromium.launch();
const page = await browser.newPage();
try {
// Safe navigation with retry
const navigated = await PlaywrightErrorHandler.withRetry(
() => PlaywrightErrorHandler.safeNavigate(page, 'https://example.com')
);
if (!navigated) {
throw new Error('Failed to navigate after retries');
}
// Try multiple selectors
const selectors = ['#primary-btn', '.submit-button', 'button[type="submit"]'];
let clicked = false;
for (const selector of selectors) {
clicked = await PlaywrightErrorHandler.safeClick(page, selector);
if (clicked) break;
}
if (!clicked) {
console.log('No clickable button found');
}
} catch (error) {
console.error(`Script failed: ${error.message}`);
} finally {
await browser.close();
}
}
advancedErrorHandling();
Best Practices for Error Handling
1. Always Use Timeouts
Set reasonable timeouts to prevent scripts from hanging indefinitely:
# Python
page.click("#button", timeout=5000) # 5 second timeout
page.goto("https://example.com", timeout=30000) # 30 second timeout
// JavaScript
await page.click('#button', { timeout: 5000 });
await page.goto('https://example.com', { timeout: 30000 });
2. Handle Network Errors
Monitor network failures and implement retry logic:
# Python
try:
page.goto("https://unreliable-site.com")
except Error as e:
if "net::" in str(e):
print("Network error detected")
# Implement retry logic
3. Graceful Resource Cleanup
Always ensure browsers and contexts are properly closed:
// JavaScript - Using try/finally
let browser;
try {
browser = await chromium.launch();
// Your automation code
} finally {
if (browser) await browser.close();
}
4. Log Errors Appropriately
Include context and actionable information in error messages:
# Python
except TimeoutError as e:
print(f"Timeout waiting for element '{selector}' on page '{page.url}'")
# Optionally take screenshot for debugging
page.screenshot(path=f"error_{int(time.time())}.png")
Proper error handling makes your Playwright scripts more reliable and easier to debug. Always anticipate potential failures and implement appropriate recovery strategies.