Handle ChromeDriver detached-document errors during selector visibility checks - #5829
Open
GlazerMann wants to merge 3 commits into
Open
GlazerMann wants to merge 3 commits into
GlazerMann wants to merge 3 commits into
Conversation
GlazerMann
marked this pull request as draft
September 22, 2026 17:32
GlazerMann
marked this pull request as ready for review
September 22, 2026 18:15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4725.
ChromeDriver can intermittently report an element from a replaced document as:
rather than the stale-element error Capybara normally knows how to recover from.
The failure recorded in #4725 occurs specifically while Capybara evaluates selector visibility:
This change handles that confirmed OOD failure at the narrowest useful boundary. When this exact ChromeDriver error occurs while evaluating a
Capybara::Selenium::ChromeNode, the detached selector candidate is treated as a non-match. Capybara's surrounding synchronized query can then continue resolving the selector against the current document.Scope and safety
The workaround is deliberately narrow.
Selenium::WebDriver::Error::UnknownErrorcontaining the specific messageNode with given id does not belong to the document.UnknownErrorto Capybara's generalinvalid_element_errors.Node::Base#synchronize.click,set,send_keys, submit, or other side-effecting WebDriver operations.UnknownErrors continue to propagate.The distinction between selector evaluation and WebDriver actions is intentional.
A candidate belonging to a replaced document cannot satisfy the current selector, so treating that candidate as a non-match is appropriate at this layer. In contrast, when ChromeDriver reports an error from a side-effecting action, the error alone does not establish whether the action already occurred. Automatically retrying such an operation could duplicate clicks, form submissions, keystrokes, or other effects.
Deliberate non-coverage
This PR does not assume that every occurrence of this ChromeDriver error originates in visibility filtering.
Capybara's Selenium lookup also contains read-only pre-filter optimizations such as
filter_by_textandgather_hints, which can execute against element references beforeSelectorQuery#matches_filters?runs.The failure recorded in #4725 does not originate from those paths, so this PR intentionally does not patch them.
If OOD later observes the same ChromeDriver error from one of those optimization paths, that case should be handled separately by abandoning the optional optimization and returning to normal selector processing rather than making
UnknownErrorgenerally retryable.Likewise, future occurrences in other read-only query paths should be evaluated from their actual stack traces rather than assuming all instances of this ChromeDriver message have identical retry semantics.
Why a selector non-match is preferable here
Returning
falsemay allow the surrounding Capybara query to continue until the normal selector timeout if no valid replacement element appears.That is intentional.
At the visibility-filter boundary, the stale candidate cannot represent a valid result from the current document. If no replacement candidate appears, the appropriate result is therefore Capybara's normal selector failure or timeout rather than an internal ChromeDriver node-reference error.
This keeps the workaround within normal query semantics without changing retry behavior for browser actions.
Compatibility and self-retirement checks
This workaround relies on a private Capybara method, so the test suite contains explicit checks that flag dependency or upstream-behavior changes requiring review.
The checks cover:
ChromeNodegaining upstream error handling relevant to this condition;matches_visibility_filters?method signature; andThe semantic canary is intended to make the workaround easier to retire. If upstream Capybara begins handling this exact visibility-filter case itself, the test suite will fail with an explicit message instructing maintainers to review and remove the workaround.
The private-method signature check remains fail-closed because blindly applying a prepend after an incompatible private API change could break selector behavior throughout the system test suite.
These checks detect Capybara/API behavior changes. They cannot prove that a future ChromeDriver has stopped emitting this error altogether, because the unit test deliberately injects the error rather than depending on reproducing a timing race.
Regression coverage
Unit tests verify that:
UnknownErrors remain visible;UnknownErroris not classified as a general Capybara invalid-element error;visible: falsebehavior is unchanged;Reproduction and validation
The original intermittent failure is documented in #4725 in:
The recorded failure used Chrome 140.0.7339.207, selenium-webdriver 4.26.0, and Capybara 3.40.0.
The stack trace from that failure reaches
ChromeNode#visible?andSelectorQuery#matches_visibility_filters?, which is the exact boundary patched here.Because this is a timing-dependent browser race, the regression tests inject the failure deterministically rather than introducing a deliberately flaky browser-level CI test. Repeated execution of the affected project-manager system test remains useful as a stress validation step.
Upstream context
Related reports exist in:
teamcapybara/capybara#2800SeleniumHQ/selenium#15401These issues provide related evidence for the same Chrome detached-document error family, but they should not be read as proof that every reported occurrence follows the same code path as OOD.
The exact OOD call path addressed by this PR is established independently by the stack trace in #4725.
Newer Capybara code has added Chrome-specific handling for this error in some paths, but reports have continued through later Chrome and Selenium versions and through paths not necessarily covered by a
ChromeNode-only change. OOD therefore keeps this workaround tied to the behavior and private API it has explicitly validated.A Chrome feature-flag workaround involving
DeferRendererTasksAfterInputhas reduced the problem for some users, but later reports indicate that it does not eliminate the failure consistently. This PR therefore does not alter Chrome scheduling behavior.The shim should be removed once OOD verifies that its supported Capybara, Selenium, and ChromeDriver combination no longer requires it. The test suite includes a canary that flags upstream behavior changes that may make the workaround obsolete.