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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

EOFError: end of file reached in a Capybara feature test usually means Ruby tried to read from a WebDriver HTTP connection that ChromeDriver, Chrome, or an intermediary had already closed. It is a symptom, not a diagnosis: a Chrome/ChromeDriver mismatch is one possibility, but browser startup, CI environment, server middleware, session reuse, or parallel profile sharing can cause the same failure.

Start by checking the exact browser and driver the test process uses, then run the failing test once with a visible browser and preserve the first ChromeDriver/Selenium error. Those checks usually distinguish a browser-startup problem from a Rails server or session-lifecycle problem more quickly than changing flags at random.

What the EOFError means

Capybara drives Chrome through Selenium and ChromeDriver. When Ruby receives an EOF while reading the WebDriver connection, the connection ended before the client received the response it expected. The exception alone does not tell you whether Chrome crashed, ChromeDriver exited, an intermediary connection failed, or the application/test setup interfered with the request.

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

That distinction matters: changing a Capybara assertion or adding a browser flag cannot fix every cause. Find the earliest failure in the driver and browser output; the Ruby EOFError may only be the final visible symptom.

Diagnose the failure in this order

1. Record the versions and environment

Capture the versions from the same CI job or shell that runs the failing test. Include Chrome, ChromeDriver, the Selenium gem, Capybara, Ruby, operating system, and CI/container image. Chrome and ChromeDriver should have matching major versions; Selenium’s Chrome guidance says that if their versions do not match, the driver will error.

ruby --version
bundle exec ruby -e 'require "selenium-webdriver"; require "capybara"; puts "Selenium #{Selenium::WebDriver::VERSION}"; puts "Capybara #{Capybara::VERSION}"'
which chromedriver
chromedriver --version

Record Chrome’s version from the actual binary available in that environment as well. The command to inspect it can differ by operating system and image, so use the executable path installed in your job rather than assuming a desktop path. Keep the output with the CI failure log; versions on a developer laptop do not establish what a container ran.

2. Confirm which ChromeDriver Selenium actually launches

which chromedriver and chromedriver --version show what your shell finds, but Selenium may resolve a different executable. Check the path and startup output from the process that runs the tests. This is especially important in older projects where a system package, Homebrew installation, gem-managed driver, or CI image may each provide a binary.

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

If the versions you inspected appear compatible but the failure remains, do not assume the check is complete: establish that Selenium launched that same binary. Correct the path or driver installation mechanism, then rerun only the failing example before making other changes.

3. Separate JavaScript tests from ordinary feature tests

Capybara pre-registers :selenium_chrome and :selenium_chrome_headless. The headless driver runs Selenium against Chrome without a visible browser window. Use a JavaScript-capable driver only for examples that need browser behavior; examples that do not need JavaScript can stay on the faster :rack_test driver.

For an RSpec feature example, opt in where needed with js: true or the explicit driver tag your suite uses. For example:

RSpec.describe "checkout", type: :feature do
  it "updates the page with JavaScript", js: true do
    visit "/checkout"
    # Exercise the browser behavior the example is intended to cover.
  end
end

This is a diagnostic and suite-organization choice, not a cure for a broken Chrome connection. If the failure occurs only in JavaScript examples, that narrows the area to inspect; it does not by itself prove that Capybara chose the wrong driver.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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

4. Run the same example with a visible browser

Temporarily switch the failing example from :selenium_chrome_headless to :selenium_chrome, keeping the same test and environment as far as possible. A visible run can make a missing executable, profile lock, unavailable display, browser crash, certificate problem, or navigation error easier to see.

If the visible browser starts and the test reaches the page while headless mode fails, focus on differences in launch configuration and CI environment. If both fail before navigation, investigate binary resolution, browser startup, libraries, and the environment before changing assertions. Restore headless mode after diagnosis if that is what the CI job requires.

5. Preserve driver logs and inspect the first error

Keep ChromeDriver and Selenium startup output in the job log. Look above the Ruby EOFError for the first browser or driver failure. An immediate driver exit commonly points to browser startup failure, incompatible binaries, missing system libraries, or restrictions in the CI environment. The later EOFError is less informative than the first process error.

Reproduce with one failing example and avoid truncating the start of the output. If your CI system separates stdout and stderr, retain both. A useful report includes the command, versions, resolved executable path, environment image, and the first relevant driver/browser message.

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.

Check launch options without masking the cause

With Selenium 4, configure Chrome through the supported Ruby options API. A minimal Capybara registration for headless Chrome looks like this:

Capybara.register_driver :selenium_chrome_headless do |app|
  options = Selenium::WebDriver::Chrome::Options.new
  options.add_argument("--headless=new")

  Capybara::Selenium::Driver.new(
    app,
    browser: :chrome,
    options: options
  )
end

Capybara.default_driver = :selenium_chrome_headless

Use the documented headless argument appropriate to the Chrome version in your environment. On Linux CI, flags such as --no-sandbox and --disable-dev-shm-usage should be added only when the environment justifies them. Record why each flag is present and test the resulting security trade-off; they are not universal fixes for EOFError.

When comparing a local run with CI, change one launch setting at a time. A set of copied flags can make a browser start without revealing the underlying issue, or can create a less secure configuration than the test environment needs.

Rule out Rails server and middleware interference

A WebDriver EOF is not automatically a ChromeDriver version problem. A published incident with the same empty-backtrace EOFError was traced to a hidden, poorly named WEBrick monkey patch. If version checks and browser startup look sound, temporarily remove custom server patches or middleware changes and compare against the standard Capybara/Puma setup.

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

Keep the application and example unchanged while isolating the server layer. If the failure disappears with the standard server setup, inspect custom patches and middleware one at a time. If it remains, return to browser/driver logs and lifecycle checks rather than concluding that the server is responsible.

Check browser-session lifecycle, parallelism, and profiles

Recreate sessions after closing the final window

If the error follows a call to close_window that closes the browser’s last window, do not continue using that same session. Discard it and create a fresh session. Capybara issue #1426 documents a stale browser object producing EOFError when a session was reused after its final window had been closed. The practical test is to rerun the example with a new session after the close rather than trying further commands against the old browser object.

Run one worker before restoring parallel tests

Execute the failing example alone. If it is stable in a single worker but fails under parallel execution, isolate browser resources before changing the test logic. Give each worker its own temporary Chrome profile and do not share one driver session across threads. Reintroduce parallelism only after the isolated single-worker run is stable.

Profile reuse and session sharing can make intermittent failures hard to diagnose because one worker may affect another’s browser state. Treat worker count and profile location as controlled variables: keep them fixed during a comparison, then change one at a time.

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

When Cuprite is a better fit

If maintaining ChromeDriver binaries is a recurring burden, Cuprite is an alternative Capybara driver for headless Chrome/Chromium that does not depend on Selenium, WebDriver, or ChromeDriver. That changes the driver stack, so compare it against your needs for CI image support and system libraries, startup-log observability, session isolation under parallel tests, JavaScript fidelity, and ongoing maintenance. It is an alternative to evaluate, not a guarantee that every EOFError will disappear.

Cuprite documents page.driver.debug for interactive diagnosis. Use that when you need to inspect what the driver is doing, and keep a minimal reproduction of the failing test so that a driver change does not silently alter what the suite exercises.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server, not a Capybara driver or a way to repair a failing test session. It can be useful for a separate check of a publicly reachable rendered page, but it does not replace the browser/driver diagnostics above. One request can capture a URL as an image or PDF; see the ScreenshotNeo API documentation for options and response details.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Replace the example URL with a publicly accessible page you are authorized to capture. ScreenshotNeo removes cookie/consent banners, newsletter popups, and chat widgets before capture; those steps can be turned off. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and responses identify page verdict and billing status in headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for AI agents and MCP clients.

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

The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots. Sign up for ScreenshotNeo’s free plan to try it.

Troubleshooting by symptom

Symptom What to check Next action
The driver exits immediately Chrome/ChromeDriver major versions, the executable Selenium resolved, missing libraries, and CI restrictions. Correct the binary or environment issue shown by the first startup error; rerun one example with logs preserved.
Only headless mode fails Headless launch argument, display assumptions, and justified Linux-specific settings. Run the same example visibly, compare startup output, then adjust only the setting implicated by the difference.
It fails after the last window closes Whether the test continues to use the same browser session after close_window. Discard that session and create a new one before continuing.
It appears only with parallel workers Shared profiles or sessions, and whether a single-worker run is stable. Use isolated temporary profiles and separate sessions; restore parallelism only after single-worker stability.
Versions match but EOFError remains Whether Selenium launched the inspected driver, server patches, and earlier driver/browser errors. Verify the resolved path, test with standard server setup, and follow the first error in the logs.

Make the fix maintainable

Once the example passes, preserve the diagnostic details that made the cause clear: the versions and image, driver path, selected Capybara driver, and any environment-specific launch flags. Keep JavaScript examples explicit and ordinary examples on :rack_test when they do not need a real browser. This makes future CI changes easier to distinguish from application regressions.

Do not treat a retry, a larger sleep, or a broad collection of Chrome flags as a confirmed fix unless it addresses an identified cause. If a failure returns, first compare the current run with the known-good browser/driver versions, executable path, server setup, session lifecycle, and worker isolation.

Frequently Asked Questions

Should I retry an EOFError automatically in CI?

A retry can show that a failure is intermittent, but it does not identify or correct the closed connection. Keep the first failing run’s browser and driver logs so you can distinguish startup, session, and environment failures.

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

Does switching to Cuprite preserve every detail of a Selenium test run?

The published Cuprite documentation describes it as a Capybara driver for headless Chrome/Chromium without Selenium, WebDriver, or ChromeDriver; it does not establish identical behavior for every suite. Validate the JavaScript interactions and CI environment your tests depend on before switching.

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.