14 KiB
		
	
	
	
	
	
	
	
			
		
		
	
	Browser Automation Troubleshooting Guide
Quick reference for common issues and solutions.
Common Errors
Element Not Found
Error: Element not found: button.submit
Causes:
- Page still loading
 - Wrong selector
 - Element in iframe
 - Element hidden/not rendered
 
Solutions:
// 1. Add wait before interaction
{action: "await_element", selector: "button.submit", timeout: 10000}
{action: "click", selector: "button.submit"}
// 2. Verify selector exists
{action: "extract", payload: "html"}
{action: "eval", payload: "document.querySelector('button.submit')"}
// 3. Check if in iframe
{action: "eval", payload: "document.querySelectorAll('iframe').length"}
// 4. Check visibility
{action: "eval", payload: "window.getComputedStyle(document.querySelector('button.submit')).display"}
Timeout Errors
Error: Timeout waiting for element after 5000ms
Solutions:
// Increase timeout for slow pages
{action: "await_element", selector: ".content", timeout: 30000}
// Wait for loading to complete first
{action: "await_element", selector: ".spinner"}
{action: "eval", payload: `
  new Promise(r => {
    const check = () => {
      if (!document.querySelector('.spinner')) r(true);
      else setTimeout(check, 100);
    };
    check();
  })
`}
// Use JavaScript to wait for specific condition
{action: "eval", payload: `
  new Promise(resolve => {
    const observer = new MutationObserver(() => {
      if (document.querySelector('.loaded')) {
        observer.disconnect();
        resolve(true);
      }
    });
    observer.observe(document.body, { childList: true, subtree: true });
  })
`}
Click Not Working
Error: Click executes but nothing happens
Causes:
- JavaScript event handler not attached yet
 - Element covered by another element
 - Need to scroll element into view
 
Solutions:
// 1. Wait longer before click
{action: "await_element", selector: "button"}
{action: "eval", payload: "new Promise(r => setTimeout(r, 1000))"}
{action: "click", selector: "button"}
// 2. Check z-index and overlays
{action: "eval", payload: `
  (() => {
    const elem = document.querySelector('button');
    const rect = elem.getBoundingClientRect();
    const topElem = document.elementFromPoint(rect.left + rect.width/2, rect.top + rect.height/2);
    return topElem === elem || elem.contains(topElem);
  })()
`}
// 3. Scroll into view first
{action: "eval", payload: "document.querySelector('button').scrollIntoView()"}
{action: "click", selector: "button"}
// 4. Force click via JavaScript
{action: "eval", payload: "document.querySelector('button').click()"}
Form Submission Issues
Error: Form doesn't submit with \n
Solutions:
// Try explicit click
{action: "type", selector: "input[name=password]", payload: "pass123"}
{action: "click", selector: "button[type=submit]"}
// Or trigger form submit
{action: "eval", payload: "document.querySelector('form').submit()"}
// Or press Enter key specifically
{action: "eval", payload: `
  const input = document.querySelector('input[name=password]');
  input.dispatchEvent(new KeyboardEvent('keypress', { key: 'Enter', keyCode: 13 }));
`}
Tab Index Errors
Error: Tab index 2 out of range
Cause: Tab closed or indices shifted
Solution:
// Always list tabs before operating on them
{action: "list_tabs"}
// After closing tabs, re-list
{action: "close_tab", tab_index: 1}
{action: "list_tabs"}
{action: "click", tab_index: 1, selector: "a"}  // Now correct index
Extract Returns Empty
Error: Extract returns empty string
Causes:
- Element not loaded yet
 - Content in shadow DOM
 - Text in ::before/::after pseudo-elements
 
Solutions:
// 1. Wait for content
{action: "await_element", selector: ".content"}
{action: "await_text", payload: "Expected text"}
{action: "extract", payload: "text", selector: ".content"}
// 2. Check shadow DOM
{action: "eval", payload: "document.querySelector('my-component').shadowRoot.querySelector('.content').textContent"}
// 3. Get computed styles for pseudo-elements
{action: "eval", payload: "window.getComputedStyle(document.querySelector('.content'), '::before').content"}
Best Practices
Selector Specificity
Use ID when available:
{action: "click", selector: "#submit-button"}  // ✅ Best
{action: "click", selector: "button.submit"}    // ✅ Good
{action: "click", selector: "button"}           // ❌ Too generic
Combine selectors for uniqueness:
{action: "click", selector: "form.login button[type=submit]"}  // ✅ Specific
{action: "click", selector: ".modal.active button.primary"}    // ✅ Specific
Use data attributes:
{action: "click", selector: "[data-testid='submit-btn']"}      // ✅ Reliable
{action: "click", selector: "[data-action='save']"}            // ✅ Semantic
Waiting Strategy
Always wait before interaction:
// ❌ BAD - No waiting
{action: "navigate", payload: "https://example.com"}
{action: "click", selector: "button"}
// ✅ GOOD - Wait for element
{action: "navigate", payload: "https://example.com"}
{action: "await_element", selector: "button"}
{action: "click", selector: "button"}
// ✅ BETTER - Wait for specific state
{action: "navigate", payload: "https://example.com"}
{action: "await_text", payload: "Page loaded"}
{action: "click", selector: "button"}
Wait for dynamic content:
// After triggering AJAX
{action: "click", selector: "button.load-more"}
{action: "await_element", selector: ".new-content"}
// After form submit
{action: "click", selector: "button[type=submit]"}
{action: "await_text", payload: "Success"}
Error Detection
Check for error messages:
{action: "click", selector: "button.submit"}
{action: "eval", payload: "!!document.querySelector('.error-message')"}
{action: "extract", payload: "text", selector: ".error-message"}
Validate expected state:
{action: "click", selector: "button.add-to-cart"}
{action: "await_element", selector: ".cart-count"}
{action: "extract", payload: "text", selector: ".cart-count"}
// Verify count increased
Data Extraction Efficiency
Use single eval for multiple fields:
// ❌ Inefficient - Multiple calls
{action: "extract", payload: "text", selector: "h1"}
{action: "extract", payload: "text", selector: ".author"}
{action: "extract", payload: "text", selector: ".date"}
// ✅ Efficient - One call
{action: "eval", payload: `
  ({
    title: document.querySelector('h1').textContent.trim(),
    author: document.querySelector('.author').textContent.trim(),
    date: document.querySelector('.date').textContent.trim()
  })
`}
Extract arrays efficiently:
{action: "eval", payload: `
  Array.from(document.querySelectorAll('.item')).map(item => ({
    name: item.querySelector('.name').textContent.trim(),
    price: item.querySelector('.price').textContent.trim(),
    url: item.querySelector('a').href
  }))
`}
Performance Optimization
Minimize navigation:
// ❌ Slow - Navigate for each item
{action: "navigate", payload: "https://example.com/item/1"}
{action: "extract", payload: "text", selector: ".price"}
{action: "navigate", payload: "https://example.com/item/2"}
{action: "extract", payload: "text", selector: ".price"}
// ✅ Fast - Use API or extract list page
{action: "navigate", payload: "https://example.com/items"}
{action: "eval", payload: "Array.from(document.querySelectorAll('.item')).map(i => i.querySelector('.price').textContent)"}
Reuse tabs:
// ✅ Keep tabs open for repeated access
{action: "new_tab"}
{action: "navigate", tab_index: 1, payload: "https://tool.com"}
// Later, reuse same tab
{action: "click", tab_index: 1, selector: "button.refresh"}
Debugging Workflows
Step 1: Check page HTML:
{action: "navigate", payload: "https://example.com"}
{action: "await_element", selector: "body"}
{action: "extract", payload: "html"}
Step 2: Test selectors:
{action: "eval", payload: "document.querySelector('button.submit')"}
{action: "eval", payload: "document.querySelectorAll('button').length"}
Step 3: Check element state:
{action: "eval", payload: `
  (() => {
    const elem = document.querySelector('button.submit');
    return {
      exists: !!elem,
      visible: elem ? window.getComputedStyle(elem).display !== 'none' : false,
      enabled: elem ? !elem.disabled : false,
      text: elem ? elem.textContent : null
    };
  })()
`}
Step 4: Check console errors:
{action: "eval", payload: "console.error.toString()"}
Patterns Library
Retry Logic
// Attempt operation with retry
{action: "click", selector: "button.submit"}
// Check if succeeded
{action: "eval", payload: "document.querySelector('.success-message')"}
// If null, retry
{action: "click", selector: "button.submit"}
{action: "await_text", payload: "Success", timeout: 10000}
Conditional Branching
// Check condition
{action: "extract", payload: "text", selector: ".status"}
// Branch based on result (in your logic)
// If "available":
{action: "click", selector: "button.buy"}
// If "out of stock":
{action: "type", selector: "input.email", payload: "notify@example.com\n"}
Pagination Handling
// Page 1
{action: "navigate", payload: "https://example.com/results"}
{action: "await_element", selector: ".results"}
{action: "eval", payload: "Array.from(document.querySelectorAll('.result')).map(r => r.textContent)"}
// Check if next page exists
{action: "eval", payload: "!!document.querySelector('a.next-page')"}
// If yes, navigate
{action: "click", selector: "a.next-page"}
{action: "await_element", selector: ".results"}
// Repeat extraction
Form Validation Waiting
// Fill form field
{action: "type", selector: "input[name=email]", payload: "user@example.com"}
// Wait for validation icon
{action: "await_element", selector: "input[name=email] + .valid-icon"}
// Proceed to next field
{action: "type", selector: "input[name=password]", payload: "password123"}
Autocomplete Selection
// Type in autocomplete field
{action: "type", selector: "input.autocomplete", payload: "San Fr"}
// Wait for suggestions
{action: "await_element", selector: ".autocomplete-suggestions"}
// Click suggestion
{action: "click", selector: ".autocomplete-suggestions li:first-child"}
// Verify selection
{action: "extract", payload: "text", selector: "input.autocomplete"}
Cookie Management
// Check if cookie exists
{action: "eval", payload: "document.cookie.includes('session_id')"}
// Set cookie
{action: "eval", payload: "document.cookie = 'preferences=dark; path=/; max-age=31536000'"}
// Clear specific cookie
{action: "eval", payload: "document.cookie = 'session_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'"}
// Get all cookies as object
{action: "eval", payload: `
  Object.fromEntries(
    document.cookie.split('; ').map(c => c.split('='))
  )
`}
XPath Examples
XPath is auto-detected (starts with / or //).
Basic XPath Selectors
// Find by text content
{action: "click", selector: "//button[text()='Submit']"}
{action: "click", selector: "//a[contains(text(), 'Learn more')]"}
// Find by attribute
{action: "click", selector: "//button[@type='submit']"}
{action: "extract", payload: "text", selector: "//div[@class='content']"}
// Hierarchical
{action: "click", selector: "//form[@id='login']//button[@type='submit']"}
{action: "extract", payload: "text", selector: "//article/div[@class='content']/p[1]"}
Advanced XPath
// Multiple conditions
{action: "click", selector: "//button[@type='submit' and contains(@class, 'primary')]"}
// Following sibling
{action: "extract", payload: "text", selector: "//label[text()='Username']/following-sibling::input/@value"}
// Parent selection
{action: "click", selector: "//td[text()='Active']/..//button[@class='edit']"}
// Multiple elements
{action: "extract", payload: "text", selector: "//h2 | //h3"}
Security Considerations
Avoid Hardcoded Credentials
// ❌ BAD - Credentials in workflow
{action: "type", selector: "input[name=password]", payload: "mypassword123"}
// ✅ GOOD - Use environment variables or secure storage
// Load credentials from secure source before workflow
Validate HTTPS
// Check protocol
{action: "eval", payload: "window.location.protocol"}
// Should return "https:"
Check for Security Indicators
// Verify login page is secure
{action: "eval", payload: `
  ({
    protocol: window.location.protocol,
    hasLock: document.querySelector('link[rel=icon]')?.href.includes('secure'),
    url: window.location.href
  })
`}
Performance Tips
Minimize Waits
// ❌ Arbitrary timeouts
{action: "eval", payload: "new Promise(r => setTimeout(r, 5000))"}
// ✅ Condition-based waits
{action: "await_element", selector: ".loaded"}
Batch Operations
// ❌ Individual extracts
{action: "extract", payload: "text", selector: ".title"}
{action: "extract", payload: "text", selector: ".author"}
{action: "extract", payload: "text", selector: ".date"}
// ✅ Single eval
{action: "eval", payload: `
  ({
    title: document.querySelector('.title').textContent,
    author: document.querySelector('.author').textContent,
    date: document.querySelector('.date').textContent
  })
`}
Reuse Browser State
// ✅ Stay logged in across operations
{action: "navigate", payload: "https://app.com/login"}
// ... login ...
{action: "navigate", payload: "https://app.com/page1"}
// ... work ...
{action: "navigate", payload: "https://app.com/page2"}
// ... work ... (still logged in)