Docs/Core Concepts/Error Handling

Error Handling

Learn how to handle errors gracefully in your Sentience automation scripts. Build resilient automation that recovers from failures and provides meaningful error messages.

Common Error Scenarios

Web automation is inherently unreliable. Networks fail, pages change, elements disappear. Proper error handling makes your automation production-ready.

Elements Not Found

The most common error: an element you're looking for doesn't exist.

from sentience import snapshot, find, click

snap = snapshot(browser)
button = find(snap, "role=button text~'submit'")

# Good - check if element exists
if button:
    click(browser, button.id)
else:
    print("Submit button not found - form might have changed")
    # Take alternative action or log error

# Also good - use try/except
try:
    if not button:
        raise ValueError("Submit button not found")
    click(browser, button.id)
except ValueError as e:
    print(f"Error: {e}")

Timeout Errors

Operations that take too long should timeout gracefully:

from sentience import wait_for

# Wait with timeout
result = wait_for(browser, "role=heading text~'Success'", timeout=10.0)

if result.found:
    print("Success message appeared!")
else:
    print(f"Timeout: Success message didn't appear within 10 seconds")
    # Log the timeout, retry, or take alternative action

# Handle long operations
try:
    result = wait_for(browser, "text~'Processing complete'", timeout=60.0)
    if not result.found:
        raise TimeoutError("Processing didn't complete in time")
except TimeoutError as e:
    print(f"Operation timed out: {e}")
    # Retry or handle failure

Network Errors

Handle network failures during page loads or API calls:

from sentience import SentienceBrowser, snapshot
import time

with SentienceBrowser(api_key="sk_...") as browser:
    max_retries = 3
    retry_count = 0

    while retry_count < max_retries:
        try:
            browser.page.goto("https://example.com", timeout=30000)
            snap = snapshot(browser)
            break  # Success
        except Exception as e:
            retry_count += 1
            print(f"Network error (attempt {retry_count}/{max_retries}): {e}")
            if retry_count >= max_retries:
                print("Failed to load page after max retries")
                raise
            time.sleep(2)  # Wait before retry

Error Handling Patterns

Retry with Exponential Backoff

import time

def retry_with_backoff(func, max_retries=3, base_delay=1):
    """Retry a function with exponential backoff"""
    for attempt in range(max_retries):
        try:
            return func()
        except Exception as e:
            if attempt == max_retries - 1:
                raise  # Last attempt failed
            delay = base_delay * (2 ** attempt)
            print(f"Retry {attempt + 1}/{max_retries} after {delay}s: {e}")
            time.sleep(delay)

# Usage
def click_submit():
    snap = snapshot(browser)
    button = find(snap, "role=button text~'submit'")
    if not button:
        raise ValueError("Button not found")
    click(browser, button.id)

retry_with_backoff(click_submit, max_retries=3, base_delay=2)

Fallback Strategies

from sentience import snapshot, find, click

snap = snapshot(browser)

# Try primary button first
submit_btn = find(snap, "role=button text~'Submit Form'")

if not submit_btn:
    # Fallback: try alternative text
    submit_btn = find(snap, "role=button text~'Submit'")

if not submit_btn:
    # Fallback: try by importance
    submit_btn = find(snap, "role=button importance>700")

if submit_btn:
    click(browser, submit_btn.id)
else:
    raise ValueError("Could not find submit button with any strategy")

Graceful Degradation

from sentience import snapshot, find, click, wait_for

def login(browser, email, password):
    """Login with error handling and graceful degradation"""
    try:
        # Navigate to login page
        browser.page.goto("https://example.com/login")

        # Wait for form to load
        result = wait_for(browser, "role=textbox text~'email'", timeout=10.0)
        if not result.found:
            print("Warning: Login form didn't load, trying alternative path")
            browser.page.goto("https://example.com/auth/login")

        # Fill form
        snap = snapshot(browser)
        email_input = find(snap, "role=textbox text~'email'")
        password_input = find(snap, "role=textbox text~'password'")

        if not email_input or not password_input:
            raise ValueError("Login form elements not found")

        type_text(browser, email_input.id, email)
        type_text(browser, password_input.id, password)

        # Submit
        submit_btn = find(snap, "role=button text~'log in'")
        if submit_btn:
            click(browser, submit_btn.id)
        else:
            # Fallback: press Enter
            press_key(browser, "Enter")

        # Verify success
        result = wait_for(browser, "role=heading text~'Dashboard'", timeout=10.0)
        return result.found

    except Exception as e:
        print(f"Login failed: {e}")
        # Take screenshot for debugging
        browser.page.screenshot(path="login_error.png")
        return False

Logging and Debugging

Structured Logging

import logging

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

# Use in automation
def click_button(browser, query):
    logger.info(f"Looking for button: {query}")
    snap = snapshot(browser)
    button = find(snap, query)

    if button:
        logger.info(f"Found button: {button.text}")
        click(browser, button.id)
        logger.info("Button clicked successfully")
    else:
        logger.error(f"Button not found: {query}")
        raise ValueError(f"Button not found: {query}")

Screenshots for Debugging

# Take screenshot on error
try:
    result = wait_for(browser, "role=button text~'submit'", timeout=5.0)
    if not result.found:
        browser.page.screenshot(path="element_not_found.png")
        raise ValueError("Submit button not found")
except Exception as e:
    browser.page.screenshot(path="error_state.png")
    raise

Best Practices

1. Always Validate Element Existence

# Good
button = find(snap, "role=button text~'submit'")
if button:
    click(browser, button.id)
else:
    # Handle missing element

# Avoid
button = find(snap, "role=button text~'submit'")
click(browser, button.id)  # Might crash if button is None

2. Use Appropriate Timeouts

# Short timeout for fast operations
result = wait_for(browser, "role=alert", timeout=2.0)

# Long timeout for slow operations
result = wait_for(browser, "text~'Report generated'", timeout=120.0)

3. Provide Meaningful Error Messages

# Good
if not button:
    raise ValueError(f"Submit button not found with query: {query}. Page might have changed.")

# Avoid
if not button:
    raise ValueError("Error")

4. Clean Up Resources

from sentience import SentienceBrowser

# Use context manager for automatic cleanup
with SentienceBrowser(api_key="sk_...") as browser:
    # Automation code
    pass  # Browser closes automatically

# Or manual cleanup
browser = SentienceBrowser(api_key="sk_...")
try:
    # Automation code
finally:
    browser.close()  # Always close

Common Error Types

ValueError

Element not found or invalid query:

try:
    button = find(snap, "role=button text~'submit'")
    if not button:
        raise ValueError("Submit button not found")
except ValueError as e:
    print(f"Validation error: {e}")

TimeoutError

Operation took too long:

try:
    result = wait_for(browser, "text~'Success'", timeout=10.0)
    if not result.found:
        raise TimeoutError("Success message didn't appear")
except TimeoutError as e:
    print(f"Timeout: {e}")

NetworkError

Network or navigation failures:

try:
    browser.page.goto("https://example.com", timeout=30000)
except Exception as e:
    print(f"Network error: {e}")
    # Retry or fail gracefully

Next Steps