Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When a Selenium test passes in a visible Chrome window but fails headlessly, first find the earliest failing WebDriver operation and inspect what the browser actually loaded. Reproduce that one test in a fresh session, record the exact browser, driver, Selenium versions and launch arguments, then save a screenshot and diagnostics before teardown. Check synchronization first; next compare browser/driver compatibility and the CI environment; then investigate viewport-dependent layout or other differences. Change one variable at a time rather than masking the failure with a longer timeout.

Start with the first failure, not the final assertion

A headless-only failure does not by itself identify its cause. The visible and headless runs may differ in timing, browser startup, viewport, resources, browser build, driver, or execution environment. Selenium’s troubleshooting documentation calls poor synchronization its most common Selenium-related error, but gives no percentage and does not establish that timing explains every headless failure. Selenium troubleshooting assistance

  1. Freeze a minimal reproduction. Run only the failing test in a fresh WebDriver session. Record the Selenium binding version, Chrome and ChromeDriver versions, OS or container image, Chrome binary path, capabilities, viewport and every command-line argument. Ensure the test calls driver.quit() during cleanup.
  2. Locate the first failing operation. Note whether it is session creation, navigation, element lookup, click or input, a wait, or an assertion. Keep the complete exception and the last successful step. A WebDriver error is not automatically a Selenium-library defect: the command travels through a browser-specific driver, so the failure may be in the driver or environment.
  3. Run a controlled comparison. Use the same test, versions, data and environment, changing only headed versus headless mode. If feasible, compare the same browser and driver locally and in CI, or compare another browser. Preserve the artifacts from both runs.
  4. Inspect the page before cleanup. Capture a screenshot, current URL and relevant DOM or text state as soon as the failure occurs. If the target element is missing or a loading state remains, investigate navigation, asynchronous content and network errors before changing the selector.
  5. Test the next required condition. A short fixed delay can be useful as a temporary diagnostic: if it changes the result, timing may be involved. Replace it with an explicit wait for the state the next command needs.
  6. Change one variable per rerun. Record what changed and whether the first failing operation moved or passed. Avoid accumulating browser flags without evidence; flags can change behavior and may be specific to a particular container or security setup.

Use an explicit wait for the state the next command needs

Page navigation completing does not necessarily mean that a JavaScript-rendered element is visible, clickable, or populated. Wait for the exact condition required by the next step: visibility before reading text, clickability before clicking, text presence before asserting, or disappearance of a loading indicator before continuing.

Selenium advises against mixing implicit and explicit waits because their timeouts can combine in unpredictable ways. Prefer an explicit wait for the relevant state rather than increasing a global timeout and hoping it covers the race. Selenium waits documentation

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Here is a runnable Python example of a headless Chrome session with an explicit wait and failure artifacts. Install Selenium with python -m pip install selenium; set TARGET_URL and TARGET_SELECTOR for the page and element under test.

import os
from pathlib import Path
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

url = os.environ["TARGET_URL"]
selector = os.environ.get("TARGET_SELECTOR", "main")

options = Options()
options.add_argument("--headless=new")
options.add_argument("--window-size=1440,1000")

driver = webdriver.Chrome(options=options)
try:
    driver.get(url)
    element = WebDriverWait(driver, 15).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, selector))
    )
    print("Loaded:", driver.current_url)
    print("Text:", element.text[:500])
except Exception:
    Path("artifacts").mkdir(exist_ok=True)
    driver.save_screenshot("artifacts/failure.png")
    Path("artifacts/page.html").write_text(
        driver.page_source, encoding="utf-8"
    )
    Path("artifacts/url.txt").write_text(driver.current_url, encoding="utf-8")
    raise
finally:
    driver.quit()

Change the condition to match the operation that follows. For example, use a clickable condition before a click, or wait for a loading element to disappear when the page signals completion that way. Keep the timeout finite and choose it for the application’s expected behavior, not as a substitute for identifying the expected state.

Verify the headless option and compare browser geometry

Selenium’s current Chrome examples use --headless=new. The flag history in Selenium’s 2023 migration post is historical: it says the newer headless mode appeared in Chrome 96, versions 96–108 used --headless=chrome, and version 109 onward used --headless=new. For current version-specific compatibility, check Selenium’s current Chrome documentation and the Chrome release in your environment rather than applying that older timeline blindly. Selenium: Headless is going away · Selenium Chrome documentation

Headless does not guarantee the same page geometry as a visible session. If the failing code depends on responsive layout or visual position, compare the actual viewport and device metrics rather than assuming the defaults match.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Set an explicit window size and record it in both runs.
  • Check whether the page switches layout at a responsive breakpoint.
  • Compare the screenshot for missing fonts, images or other resources.
  • Check that the Chrome binary path and any configured log paths exist on the machine that launches Chrome.

Treat these as hypotheses, not universal causes. Change only the suspected geometry or resource variable and rerun the same reproduction.

Separate Chrome, ChromeDriver and environment problems

WebDriver sends browser commands through a browser-specific driver. If session creation fails, or commands behave differently across environments, record the exact Chrome and ChromeDriver versions and compare the first failing command. A test that works in another browser or on another machine helps narrow the layer involved; it does not by itself prove the root cause.

Selenium Manager is built into Selenium. Selenium’s guide says it resolves and caches a matching driver starting with Selenium 4.6, and can download a browser if one is absent starting with Selenium 4.11. This can simplify driver setup, but still record the resolved versions and confirm the browser binary and execution image are the ones you intended. Selenium Manager documentation

For local-versus-remote comparisons, preserve the same test and note which machine launches the browser. Selenium supports remote WebDriver sessions; the browser, driver, fonts, network and viewport belong to the remote execution environment, not necessarily the machine running the test code. Selenium WebDriver sessions and drivers

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture useful evidence at the point of failure

A screenshot can show whether headless Chrome rendered a different layout, stayed on an intermediate page, or displayed an error. Pair it with the current URL and DOM or text state; no single artifact explains every failure. Selenium’s Chrome examples demonstrate screenshots, and its coding guidance points to WebDriver BiDi for browser console logs, JavaScript errors and network interception. Check whether the Selenium binding version and configuration you use support the BiDi features you need. Chrome screenshots · WebDriver BiDi

  • Save the full exception and the last successful test step.
  • Record the current URL, screenshot and relevant DOM state before quitting the session.
  • Include browser and driver logs when configured, plus console and network evidence when available.
  • Report exact versions, launch arguments, capabilities, OS or container image, binary path and viewport so another person can reproduce the run.

Troubleshoot by symptom

Symptom What to check Next action
Session fails before the page opens Chrome binary path, startup arguments, browser/driver versions, and the machine or container launching Chrome. Save the complete session-creation exception and browser/driver logs; verify the binary exists and compare the resolved versions.
Element lookup fails or returns no usable element Screenshot, current URL, DOM state, navigation completion, asynchronous content and selector scope. Confirm the page reached the expected state, then wait for the relevant element condition before interacting.
Click or input fails only headlessly Whether the element is visible and interactable, whether an overlay is present, and whether layout differs at the headless viewport. Capture the page at failure, compare viewport settings, and wait for the required interaction condition.
Assertion fails with unexpected text or page content Whether the page loaded an intermediate/error page, content is asynchronous, or a request failed. Record URL and DOM/text state; inspect console and network evidence when available before altering the assertion.
Local run passes but CI fails Browser and driver versions, image/container, paths, fonts/resources, network, viewport and launch arguments. Reproduce using the CI image and keep its artifacts; change one environment variable at a time.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is a page screenshot rather than debugging Selenium interaction, ScreenshotNeo provides a screenshot API and MCP server. It accepts a URL in one GET request and returns an image or PDF; its browser workflow is separate from a Selenium test, so it is not a substitute for investigating a failed WebDriver command.

For an independent screenshot capture, the Python request below saves the response body as a WebP file. See the ScreenshotNeo documentation for request options and response details.

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)

ScreenshotNeo removes cookie/consent banners, newsletter popups and chat widgets before capture; those steps can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server includes screenshot, page-info and PDF-capture tools for AI agents. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Sign up for ScreenshotNeo’s free plan to try 1,000 screenshots a month without a card.

A practical stopping point

Once you can reproduce the first failing operation and preserve the page state at that moment, use the evidence to decide whether the issue is a wait condition, Chrome/driver startup, environment drift, or different page geometry. If the cause remains uncertain, share the versions, arguments and artifacts rather than presenting an unverified flag or timeout change as a fix.

Frequently Asked Questions

Does a headless-only failure prove Chrome is broken?

No. The failing command may involve the browser-specific driver, page timing, environment, or geometry; isolate the layer with controlled comparisons.

Should I add a longer sleep to fix the test?

Use a fixed delay only as a temporary diagnostic. Replace it with an explicit wait for the state required by the next operation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Can ScreenshotNeo debug a Selenium interaction failure?

No. It captures pages from a URL; it does not replace WebDriver diagnostics for a failing Selenium command.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.