Common issues and solutions for the Browser Controller.
BrowserLaunchError: Failed to launch browser
Solutions:
-
Check browser installation
# Verify Chrome is installed google-chrome --version # or "C:\Program Files\Google\Chrome\Application\chrome.exe" --version
-
Try different browser
# If Chrome fails, try Firefox config = BrowserConfig(browser_type=BrowserType.FIREFOX)
-
Check WebDriver compatibility
pip install --upgrade webdriver-manager selenium
-
System permissions
config = BrowserConfig( browser_options={ "no_sandbox": True, # For Linux systems "disable_dev_shm_usage": True } )
SessionNotCreatedException: This version of ChromeDriver only supports Chrome version X
Solution:
# Update webdriver-manager to get latest drivers
pip install --upgrade webdriver-managerelement = await session.find_element("button.submit")
# Returns NoneSolutions:
-
Use explicit waits
# Wait for element to appear element = await session.wait_for_element("button.submit", timeout=30)
-
Check selector accuracy
# Use browser dev tools to verify selector # Try different selector strategies element = await session.find_element("//button[contains(@class, 'submit')]") # XPath element = await session.find_element("input[type='submit']") # CSS
-
Wait for page load
await session.navigate_to("https://example.com") # Wait for page to fully load await session.wait_for_element("body", timeout=30)
success = await session.click_element("button")
# Returns FalseSolutions:
-
Wait for element to be clickable
from selenium.webdriver.support import expected_conditions as EC element = await session.wait_for_condition( EC.element_to_be_clickable(("css selector", "button")), timeout=30 )
-
Scroll element into view
element = await session.find_element("button") if element: await session.execute_script( "arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", element ) await asyncio.sleep(1) await session.click_element("button")
-
Check for overlays
# Close any modal or overlay first modal_close = await session.find_element(".modal-close, .overlay-close") if modal_close: await session.click_element(".modal-close")
TimeoutException: Page load timeout
Solutions:
-
Increase timeout
config = BrowserConfig( page_load_timeout=60, # Increase from default 30 implicit_wait=20 )
-
Handle slow loading sites
try: await session.navigate_to("https://slow-site.com", timeout=60) except Exception as e: print(f"Page load timeout: {e}") # Continue with partial load
element = await session.find_element(".dynamic-content")
# Returns None even though element existsSolutions:
-
Wait for specific content
# Wait for AJAX content element = await session.wait_for_element(".dynamic-content", timeout=30) # Wait for text to appear from selenium.webdriver.support import expected_conditions as EC await session.wait_for_condition( EC.text_to_be_present_in_element(("css selector", ".status"), "Ready"), timeout=30 )
-
Handle loading indicators
# Wait for loading spinner to disappear await session.wait_for_element_to_disappear(".loading-spinner", timeout=30)
SessionError: Maximum sessions reached
Solutions:
-
Close unused sessions
# Get and close old sessions active_sessions = controller.get_active_sessions() for session_id in active_sessions[:-5]: # Keep only last 5 await controller.close_session(session_id)
-
Use session context managers
# Automatically cleaned up async with controller.new_session() as session: await session.navigate_to("https://example.com") # Session auto-closed when block exits
Solutions:
-
Monitor session count
print(f"Active sessions: {controller.get_session_count()}") if controller.get_session_count() > 10: # Close some sessions pass
-
Use headless mode
config = BrowserConfig(headless=True) # Uses less memory
ValidationError: Invalid browser configuration
Solutions:
-
Check configuration values
# Valid browser types config = BrowserConfig( browser_type=BrowserType.CHROME, # Use enum headless=True, # Boolean not string window_size=(1280, 720), # Tuple not list )
-
Validate configuration
try: config = BrowserConfig(**config_dict) except ValidationError as e: print(f"Configuration error: {e}")
config = BrowserConfig() # Uses defaults instead of .envSolutions:
-
Load environment explicitly
from dotenv import load_dotenv load_dotenv() # Load .env file config = BrowserConfig() # Now uses env vars
-
Check .env file location
# .env should be in project root ls -la .env
Solutions:
-
Set log level
import os os.environ["LOG_LEVEL"] = "DEBUG" # Or configure directly from src.utils.logger import configure_logging configure_logging(level="DEBUG")
-
Check log file location
# Logs are saved to logs/ directory ls -la logs/
Solutions:
- Configure rotation
configure_logging( file_rotation="10 MB", retention="7 days" )
import os
os.environ["LOG_LEVEL"] = "DEBUG"
from src.utils.logger import get_logger
logger = get_logger("Debug")
# Log everything
logger.debug("Starting automation")async def debug_automation():
async with BrowserController(config) as controller:
session = await controller.create_session()
try:
await session.navigate_to("https://example.com")
await session.take_screenshot("01_loaded.png")
await session.click_element("button")
await session.take_screenshot("02_clicked.png")
finally:
await controller.close_session(session.session_id)async def check_element_state(session, selector):
element = await session.find_element(selector)
if element:
visible = await session.is_element_visible(selector)
enabled = await session.is_element_enabled(selector)
text = await session.get_element_text(selector)
print(f"Element {selector}:")
print(f" Visible: {visible}")
print(f" Enabled: {enabled}")
print(f" Text: {text}")
else:
print(f"Element not found: {selector}")# Enable logging of network requests (Chrome only)
config = BrowserConfig(
browser_options={
"enable_logging": True,
"log_level": "INFO"
}
)Solutions:
-
Disable images
config = BrowserConfig( browser_options={"disable_images": True} )
-
Use mobile viewport
config = BrowserConfig( window_size=(390, 844), # Mobile size browser_options={"mobile_emulation": {"deviceName": "iPhone X"}} )
Solutions:
-
Limit concurrent sessions
MAX_SESSIONS = 5 if controller.get_session_count() >= MAX_SESSIONS: await controller.close_session(oldest_session_id)
-
Use browser restart strategy
# Restart browser every N operations operations_count = 0 RESTART_AFTER = 100 if operations_count >= RESTART_AFTER: await controller.close() await controller.launch() operations_count = 0
Solutions:
-
Optimize waits
# Use shorter, specific waits element = await session.wait_for_element("button", timeout=5) # Instead of long implicit wait config = BrowserConfig(implicit_wait=2) # Shorter default
-
Batch operations
# Find all elements at once elements = await session.find_elements(".item") # Process them efficiently for i, element in enumerate(elements): text = await session.get_element_text(f".item:nth-child({i+1})")
async def health_check():
"""Comprehensive health check"""
print("🏥 Browser Controller Health Check")
print("=" * 40)
# Test 1: Basic import
try:
from src.core.browser_controller import BrowserController
print("✅ Import successful")
except Exception as e:
print(f"❌ Import failed: {e}")
return
# Test 2: Configuration
try:
from src.config.browser_config import BrowserConfig
config = BrowserConfig()
print("✅ Configuration successful")
except Exception as e:
print(f"❌ Configuration failed: {e}")
return
# Test 3: Browser launch
try:
async with BrowserController(config) as controller:
print("✅ Browser launch successful")
# Test 4: Session creation
session = await controller.create_session()
print("✅ Session creation successful")
# Test 5: Navigation
await session.navigate_to("https://httpbin.org/html")
title = await session.get_title()
print(f"✅ Navigation successful: {title}")
await controller.close_session(session.session_id)
print("✅ Session cleanup successful")
except Exception as e:
print(f"❌ Browser test failed: {e}")
return
print("🎉 All health checks passed!")
# Run health check
asyncio.run(health_check())def check_environment():
"""Check system environment"""
import sys
import subprocess
print(f"Python version: {sys.version}")
print(f"Platform: {sys.platform}")
# Check Chrome
try:
result = subprocess.run(
["google-chrome", "--version"],
capture_output=True,
text=True
)
print(f"Chrome: {result.stdout.strip()}")
except:
print("Chrome: Not found or not in PATH")
# Check dependencies
deps = ["selenium", "webdriver-manager", "pydantic", "loguru"]
for dep in deps:
try:
__import__(dep)
print(f"✅ {dep}: Installed")
except ImportError:
print(f"❌ {dep}: Missing")
check_environment()import os
os.environ["LOG_LEVEL"] = "DEBUG"
# Run your automation
# Check logs/ directory for detailed logs# System info
uname -a # Linux/Mac
systeminfo # Windows
# Python info
python --version
pip list | grep -E "(selenium|webdriver|pydantic|loguru)"
# Browser info
google-chrome --version
firefox --versionasync def minimal_repro():
"""Minimal code that reproduces the issue"""
config = BrowserConfig(browser_type=BrowserType.CHROME, headless=True)
async with BrowserController(config) as controller:
session = await controller.create_session()
try:
# Minimal steps to reproduce issue
await session.navigate_to("https://httpbin.org/html")
element = await session.find_element("h1")
print(f"Found element: {element is not None}")
finally:
await controller.close_session(session.session_id)
asyncio.run(minimal_repro())If you encounter issues not covered here, check the logs first, then create a minimal reproduction case to help with diagnosis.