The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →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.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11That 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.
#1 Best Overall
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.
Recommended Free Tools
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.
Rank #2
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.
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.
Rank #3
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.
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.
Rank #4
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.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →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.
Best Value
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.
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.
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.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsDoes 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.
Quick Recap
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.

