This guide covers common issues and solutions when using chrome-driver.
- Chrome Won't Start
- Connection Issues
- WebSocket Errors
- Element Not Found
- Screenshot/PDF Problems
- Content Extraction Issues
- Performance Problems
- Platform-Specific Issues
Error: Could not start Chrome
Error: Chrome not found
1. Verify Chrome is installed:
# macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --version
# Linux
google-chrome --version
# or
chromium --version
# WSL
/mnt/c/Program\ Files/Google/Chrome/Application/chrome.exe --version2. Try starting Chrome manually:
google-chrome --remote-debugging-port=9222 --headless --user-data-dir=/tmp/chrome-test3. Check for port conflicts:
# See if port 9222 is already in use
lsof -i :9222
# Kill process using the port
kill -9 <PID>4. Kill orphan Chrome processes:
pkill -f 'chrome.*--remote-debugging-port'
# Or more aggressive (kills all Chrome)
pkill chrome5. Specify Chrome location explicitly:
my $chrome = ChromeDriver->new(
chrome_binary => '/path/to/chrome',
port => 9222
);Error: Could not connect to Chrome
Error: WebSocket handshake failed
Error: Connection refused
1. Check if Chrome is running:
curl -s http://localhost:9222/json/versionShould return JSON with Chrome version and WebSocket URL.
2. Verify debugging is enabled:
Chrome must be started with --remote-debugging-port=9222
3. Check firewall settings:
- Ensure localhost connections are allowed
- Port 9222 should not be blocked
4. Check for SSL/certificate issues:
Chrome DevTools Protocol uses ws:// (not wss://) on localhost, so SSL shouldn't be an issue. If it is, check for proxy/VPN interference.
5. Restart Chrome:
$chrome->restart(); # If method exists
# or
system("pkill -f 'chrome.*--remote-debugging-port'");
sleep 2;
# Start new instanceError: WebSocket frame error
Error: Invalid handshake response
Error: Connection closed unexpectedly
1. Check Chrome version:
google-chrome --versionEnsure Chrome is reasonably up-to-date (version 90+).
2. Verify WebSocket endpoint:
curl -s http://localhost:9222/json | jq '.[0].webSocketDebuggerUrl'3. Test manual WebSocket connection:
# Install websocat if needed
websocat "$(curl -s http://localhost:9222/json | jq -r '.[0].webSocketDebuggerUrl')"
# Send test message
{"id":1,"method":"Browser.getVersion"}4. Check for proxy interference: WebSocket connections can be blocked by proxies. Temporarily disable proxy:
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXYError: Element not found: selector
Error: querySelector returned null
1. Wait for element to load:
# Bad - element might not exist yet
my $el = $dom->query('.dynamic-element');
# Good - wait for it
my $el = $dom->wait_for('.dynamic-element', 10);
die "Element not found" unless $el;2. Verify selector in browser DevTools:
- Open page in Chrome
- Press F12 to open DevTools
- In Console, test selector:
document.querySelector('your-selector')
3. Check for iframes: Content in an iframe requires switching frames first:
# Get frame
my $frame_id = $chrome->send('Page.getFrameTree')->{result}{frameTree}{frame}{id};
# Execute in frame context
$chrome->send('Runtime.evaluate', {
expression => 'document.querySelector("selector")',
contextId => $frame_id
});4. Wait for page load:
$nav->goto('https://example.com');
$chrome->wait_for_event('Page.loadEventFired', 30);
# Now query elements5. Use more specific selectors:
# Bad - too generic
$dom->query('div');
# Good - specific
$dom->query('div.main-content > article#post-123');Error: Screenshot failed
Error: PDF generation failed
Empty/blank screenshots or PDFs
1. Ensure page is loaded:
$nav->goto('https://example.com');
$chrome->wait_for_event('Page.loadEventFired', 30);
sleep 1; # Extra time for rendering
$capture->screenshot(file => '/tmp/page.png');2. Check viewport size:
# Set explicit viewport before screenshot
$capture->set_viewport(1920, 1080);
$capture->screenshot(file => '/tmp/page.png');3. Enable background graphics for PDF:
$pdf->pdf(
file => '/tmp/page.pdf',
print_background => 1 # Include backgrounds
);4. Wait for images to load:
# Wait for all images
$js->evaluate(qq{
Promise.all(
Array.from(document.images)
.filter(img => !img.complete)
.map(img => new Promise(resolve => {
img.onload = img.onerror = resolve;
}))
)
});5. Check file permissions:
# Ensure output directory is writable
ls -la /tmp/
touch /tmp/test-write && rm /tmp/test-write6. Try different format:
# PNG is most reliable
$capture->screenshot(
file => '/tmp/page.png',
format => 'png'
);Empty content returned
Incomplete text extraction
Markdown conversion errors
1. Wait for content to load:
$nav->goto('https://example.com');
$chrome->wait_for_event('Page.loadEventFired', 30);
# Wait for specific element
$dom->wait_for('article', 10);
# Now extract
my $content = $content->markdown();2. Use specific selectors:
# Bad - might get navigation/footer/ads
my $text = $content->text();
# Good - target main content
my $text = $content->text('article.main-content');3. Check for JavaScript-rendered content: Some pages load content via JavaScript. Add a delay:
$nav->goto('https://example.com');
sleep 2; # Wait for JS to execute
my $content = $content->markdown();4. Handle infinite scroll:
# Scroll to bottom to trigger content load
$js->evaluate(qq{
window.scrollTo(0, document.body.scrollHeight);
});
sleep 1;
my $content = $content->markdown();5. Disable JavaScript if it's problematic:
$chrome->send('Emulation.setScriptExecutionDisabled', { value => 1 });
$nav->goto('https://example.com');Slow page loads
High memory usage
Chrome becomes unresponsive
1. Use headless mode:
my $chrome = ChromeDriver->new(headless => 1); # Faster2. Disable images:
$chrome->send('Network.setBlockedURLs', {
urls => ['*.jpg', '*.jpeg', '*.png', '*.gif', '*.webp']
});3. Set shorter timeouts:
$nav->goto('https://example.com', timeout => 10); # Don't wait forever4. Close pages when done:
$chrome->close(); # Free memory5. Limit cache size:
my $chrome = ChromeDriver->new(
user_data_dir => '/tmp/chrome-minimal', # Temporary profile
headless => 1
);6. Restart Chrome periodically: For long-running scripts, restart Chrome every N pages:
for my $i (1..100) {
$nav->goto($urls[$i]);
# ... do work ...
if ($i % 10 == 0) {
$chrome->close();
sleep 1;
$chrome = ChromeDriver->new(headless => 1);
$chrome->connect_to_page();
}
}Chrome location issues:
my $chrome = ChromeDriver->new(
chrome_binary => '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
);Permission dialogs: Grant Terminal/iTerm access to "Developer Tools" in System Preferences.
Missing libraries:
# Ubuntu/Debian
sudo apt-get install -y google-chrome-stable
# Or Chromium
sudo apt-get install -y chromium-browser
# Check dependencies
ldd $(which google-chrome)No display (headless server):
# Install Xvfb
sudo apt-get install -y xvfb
# Run with virtual display
xvfb-run google-chrome --remote-debugging-port=9222Chrome path:
my $chrome = ChromeDriver->new(
chrome_binary => '/mnt/c/Program Files/Google/Chrome/Application/chrome.exe'
);Display issues:
# Install VcXsrv or similar X server on Windows
export DISPLAY=:0Slow networking: WSL1 has slow network performance. Use WSL2:
wsl --set-version Ubuntu 2# Add debug output
$chrome->{debug} = 1; # If supported
# Or manually log CDP traffic
my $original_send = \&ChromeDriver::send;
*ChromeDriver::send = sub {
my ($self, $method, $params) = @_;
warn ">>> $method: " . JSON::PP->new->encode($params) . "\n";
my $result = $original_send->($self, $method, $params);
warn "<<< " . JSON::PP->new->encode($result) . "\n";
return $result;
};perl -vRequires Perl 5.14+.
perl -e 'use IO::Socket::INET; print "OK\n"'
perl -e 'use HTTP::Tiny; print "OK\n"'
perl -e 'use JSON::PP; print "OK\n"'
perl -e 'use Digest::SHA; print "OK\n"'
perl -e 'use MIME::Base64; print "OK\n"'All should print "OK".
Run the interactive help system:
use Help::Browser qw(browser_help);
print browser_help(); # Overview
print browser_help('topic'); # Specific topic- Include error message
- Include Perl version (
perl -v) - Include Chrome version (
google-chrome --version) - Include platform (macOS/Linux/WSL)
- Include minimal reproduction code
Submit issues at: https://github.com/Focus-AI/chrome-driver/issues