PRACTICAL GUIDE / FluentWait ignored exception final cause
Why your FluentWait timeout ends with the wrong-looking cause
Learn how Selenium selects a FluentWait timeout cause, record every poll safely, and ignore only exceptions the application can recover from.
In this guide6 sections
What you will learn
- Why the timeout carries only one cause
- How to record the polls Selenium does not retain
- Three failures that need different fixes
- How to separate this problem from the near-misses
Checkout passes on a laptop, but CI times out while waiting for the payment status and reports only a Selenium TimeoutException. The useful line sits lower in the exception chain: the final poll hit a StaleElementReferenceException after the page replaced the status node. Adding RuntimeException to the ignore list makes the job slower and turns unrelated defects into the same timeout.
Why the timeout carries only one cause
An explicit wait is a polling loop around a condition. Each FluentWait instance has an input object, a timeout, a polling interval, an optional message, and a list of exception types that may be ignored. In a browser test, the input is usually WebDriver, although the class is generic and can poll any Java object.
The until method evaluates the condition before deciding whether to sleep again. A non-null value succeeds, except that Boolean false does not. A null result or false result means "not ready yet." An exception takes one of two paths. If its type is not on the ignore list, Selenium propagates it immediately. If its type matches an ignored class, Selenium catches it and gives the condition another chance while time remains.
Matching includes subclasses. Ignoring WebDriverException is therefore not a convenient way to cover a few browser races. It makes every subclass eligible for suppression, including errors that describe a dead or invalid browser session. The page cannot repair those failures during the next poll.
The subtle part is the local variable Selenium uses for timeout construction. An ignored exception becomes the current last exception. A later ignored exception replaces it. Selenium does not append either one to a collection, and Throwable.getSuppressed() is not a back door to that history. The word "suppressed" in the FluentWait Javadoc describes an exception caught during polling, not a call to Throwable.addSuppressed.
A non-throwing poll that returns null or false clears the remembered exception. That rule matters in real pages. Suppose the first poll cannot find a status element, the second finds it with text "Pending," and every later poll also returns null because "Paid" has not appeared. The wait can end with getCause() equal to null even though NoSuchElementException was ignored earlier. The last evaluated condition did not fail because of the missing element, so Selenium does not present that earlier exception as the cause of the timeout.
If the final evaluation throws an ignored StaleElementReferenceException instead, timeout construction receives that single exception. TimeoutException.getCause() then exposes it. The result is a final cause, not the most frequent cause, first cause, root cause, or complete chronology. Treating it as any of those creates confident but wrong incident reports.
Current Selenium Java code checks the timeout after evaluating the condition. That ordering lets a condition get an initial attempt, including with a zero timeout. Once the timeout boundary has passed, Selenium builds a message that includes the configured duration and polling interval, then constructs the timeout with the current last exception. WebDriverWait adds driver details and, when available, session and capability information. Those extra fields help correlate a run, but they do not add poll history.
The configured interval is not a promise about the exact distance between poll starts. Selenium sleeps for the interval after the condition finishes, so locator execution, a remote Grid round trip, and application-side script work add to the gap. This becomes especially important when an implicit wait is active. One findElement call inside the condition can block before FluentWait gets to its own sleep.
Java's WebDriverWait is a specialization of FluentWait. Its constructors add NotFoundException to the ignore list by default. NoSuchElementException is a subclass of NotFoundException, so a missing element inside a WebDriverWait condition is normally retried. A directly constructed FluentWait does not gain that default automatically; the test must state the ignore policy itself.
That difference explains two stack traces that look inconsistent. One helper built with WebDriverWait may time out with NoSuchElementException as its cause. Another helper built with plain FluentWait may throw the same NoSuchElementException on its first evaluation. Neither behavior is random. The wait types were configured differently.
The practical review question is not "Which exceptions can we ignore?" Ask whether another evaluation can succeed without changing the test setup. A status element that is temporarily absent while a documented render transition completes can meet that test. An invalid selector, closed window, dead session, failed assertion, or JSON parsing bug cannot. Those should remain immediate failures.
How to record the polls Selenium does not retain
Start diagnosis with three records: the timeout itself, its cause, and the sequence produced by the condition. The first two come from Selenium. The third has to come from your framework because FluentWait intentionally stores no ledger.
A useful recorder stays outside the condition's business rule. It notes the attempt number, elapsed duration, outcome category, and exception class. It then returns the original value or rethrows the original unchecked failure. That last point preserves Selenium's ignore decision. A wrapper that catches an exception and returns false would change the behavior it claims to observe.
The following Java utility records runtime exceptions and errors without serializing WebElement values, page text, cookies, or credentials. It can be compiled as a regular class in a Java 17 or newer test project.
package example.waits;
import java.time.Duration;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
public final class PollRecorder {
public record Poll(int attempt, Duration elapsed, String outcome, String detail) {}
private PollRecorder() {}
public static <T, V> Function<T, V> observe(
Function<T, V> condition, List<Poll> polls) {
Objects.requireNonNull(condition, "condition");
Objects.requireNonNull(polls, "polls");
long startedAt = System.nanoTime();
AtomicInteger attempts = new AtomicInteger();
return input -> {
int attempt = attempts.incrementAndGet();
try {
V value = condition.apply(input);
polls.add(new Poll(
attempt,
Duration.ofNanos(System.nanoTime() - startedAt),
outcomeOf(value),
""));
return value;
} catch (RuntimeException | Error failure) {
polls.add(new Poll(
attempt,
Duration.ofNanos(System.nanoTime() - startedAt),
"threw",
failure.getClass().getName() + ": "
+ String.valueOf(failure.getMessage())));
throw failure;
}
};
}
private static String outcomeOf(Object value) {
if (value == null) {
return "null";
}
if (value instanceof Boolean result) {
return result ? "true" : "false";
}
return "value:" + value.getClass().getSimpleName();
}
}Use a fresh list for every wait. FluentWait makes no thread-safety guarantee, and sharing one mutable ledger across parallel tests would mix unrelated attempts. The recorder above also uses a monotonic elapsed-time source, System.nanoTime, rather than wall-clock timestamps. That keeps duration ordering useful if the machine clock changes.
Here is a narrow payment-status wait using the recorder. The condition locates the element on every poll, which is necessary if the application is allowed to replace that node. It ignores only absence and staleness, and only because this example's page contract says the status widget may be mounted late and replaced during checkout finalization.
package example.checkout;
import example.waits.PollRecorder;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Wait;
public final class PaymentStatusWait {
private static final By STATUS = By.cssSelector("[data-testid='payment-status']");
private PaymentStatusWait() {}
public static WebElement untilPaid(WebDriver driver) {
List<PollRecorder.Poll> polls = new ArrayList<>();
Wait<WebDriver> wait = new FluentWait<>(driver)
.withTimeout(Duration.ofSeconds(10))
.pollingEvery(Duration.ofMillis(250))
.withMessage("payment status to become Paid")
.ignoring(NoSuchElementException.class)
.ignoring(StaleElementReferenceException.class);
try {
return wait.until(PollRecorder.observe(currentDriver -> {
WebElement status = currentDriver.findElement(STATUS);
return "Paid".equals(status.getText()) ? status : null;
}, polls));
} catch (TimeoutException timeout) {
System.err.println("timeoutCause="
+ (timeout.getCause() == null
? "<none>"
: timeout.getCause().getClass().getName()));
polls.forEach(poll -> System.err.printf(
"poll=%d elapsed=%s outcome=%s detail=%s%n",
poll.attempt(), poll.elapsed(), poll.outcome(), poll.detail()));
throw timeout;
}
}
}Given those configured values, Selenium's timeout message begins with the custom expectation and says it tried for 10 seconds with a 250 millisecond interval. Build and environment lines may follow. A helper built with WebDriverWait can also add driver, session, and capability information through its timeout override. If the last poll threw an ignored stale-element error, the Java stack trace contains a "Caused by" section for StaleElementReferenceException. If the last poll returned null, that section is absent even when the recorder shows earlier ignored errors.
Do not publish the recorder's raw exception messages automatically. A locator error is usually harmless, but driver messages and application text can contain URLs, account identifiers, or test data. Log the exception class by default. Allow the full message only in a protected artifact whose retention and access match the test-data policy.
The ledger answers questions getCause cannot. Did every evaluation fail the same way? Did the element appear and remain in the wrong state? Did each condition call take several seconds before the configured sleep? Did the exception change after a rerender? The answers lead to different fixes.
Three failures that need different fixes
Consider a React checkout page that renders "Authorizing," removes the status node, then mounts a new node with "Paid." A page object captured the original WebElement before clicking Pay. Every later call to getText uses the obsolete reference. Ignoring StaleElementReferenceException around that cached object only repeats an operation that can never succeed.
The repair is to retain the locator, not the element. The PaymentStatusWait example does this by calling findElement inside the condition. Staleness becomes recoverable because a future poll can receive the new node. The trade-off is one locator call per evaluation and the risk of matching a different node if the selector is loose. A stable data-testid or similarly specific contract matters more after this change.
Even then, do not add staleness to a global helper merely because one page replaces a node. Another page may throw it because a component reference escaped its intended lifecycle. Keep the ignore rule beside the transition that makes recovery plausible. That local placement lets a reviewer connect policy to page behavior.
The second failure produces a timeout with no cause after an ignored exception. This small executable program demonstrates the rule without a browser. Its first evaluation throws NoSuchElementException, and every later evaluation returns null. The program fails if Selenium attaches the earlier exception after the final null result.
package example.waits;
import java.time.Duration;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.concurrent.atomic.AtomicInteger;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.support.ui.FluentWait;
public final class ClearedCauseDemo {
private ClearedCauseDemo() {}
public static void main(String[] args) {
AtomicInteger calls = new AtomicInteger();
MutableClock clock = new MutableClock();
FluentWait<String> wait =
new FluentWait<>("payment-status", clock, clock::advance)
.withTimeout(Duration.ofSeconds(1))
.pollingEvery(Duration.ofMillis(100))
.ignoring(NoSuchElementException.class);
try {
wait.until(input -> {
if (calls.getAndIncrement() == 0) {
throw new NoSuchElementException(input + " was not mounted");
}
return null;
});
throw new AssertionError("The condition should not succeed");
} catch (TimeoutException timeout) {
if (timeout.getCause() != null) {
throw new AssertionError("Expected a cleared cause", timeout);
}
System.out.println("polls=" + calls.get() + ", timeoutCause=<none>");
}
}
private static final class MutableClock extends Clock {
private final ZoneId zone;
private Instant current;
private MutableClock() {
this(ZoneOffset.UTC, Instant.EPOCH);
}
private MutableClock(ZoneId zone, Instant current) {
this.zone = zone;
this.current = current;
}
@Override
public ZoneId getZone() {
return zone;
}
@Override
public Clock withZone(ZoneId newZone) {
return new MutableClock(newZone, current);
}
@Override
public Instant instant() {
return current;
}
private void advance(Duration duration) {
current = current.plus(duration);
}
}
}This example uses Selenium's public Clock and Sleeper constructor so it does not depend on scheduler timing. The sleeper advances a private virtual clock instead of pausing the thread. After the first ignored exception, every evaluation returns null; the virtual timeout therefore receives no cause. Production waits should use the normal constructor and system sleeper.
In a browser suite, this pattern often means the page moved through more than one state. The widget might be absent, then present with "Declined" while the condition waits only for "Paid." The right failure report should preserve both facts. Reporting only the early missing-element exception blames rendering and hides the observed business state. A poll recorder shows the transition, and a final screenshot or DOM snapshot confirms what the user could see.
The fix is not to force an exception out of the last poll. Keep null or false as the honest "condition not satisfied" result. Add a domain-specific message such as "payment status to become Paid," and record the last observed status separately. This costs a small amount of framework code, but it keeps the exception cause semantically truthful.
The third failure starts after the browser session has died. Perhaps a Grid node restarted, teardown ran early, or another test shared and quit the driver. The condition throws a session-related WebDriverException on every attempt. With a narrow ignore list, that exception propagates on the first evaluation and points at browser ownership. With WebDriverException or RuntimeException ignored, the same deterministic failure is retried until the explicit timeout and then attached under TimeoutException.
That conversion is harmful. Dashboards may classify the run as an application synchronization failure. A retry may launch a fresh session and pass, erasing evidence that the original session lifecycle was broken. Meanwhile, every suppressed poll sends another command toward a session that cannot recover.
Use WebDriverWait when its default NotFoundException policy matches the condition, then add only the one extra transient type justified by the page. This method allows a temporarily missing or replaced receipt, while a dead session, invalid selector, or assertion defect still stops immediately.
package example.checkout;
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.WebDriverWait;
public final class ReceiptWait {
private static final By RECEIPT =
By.cssSelector("[data-testid='receipt-number']");
private ReceiptWait() {}
public static WebElement untilReceiptHasText(WebDriver driver) {
return new WebDriverWait(driver, Duration.ofSeconds(8))
.withMessage("receipt number to contain non-blank text")
.ignoring(StaleElementReferenceException.class)
.until(currentDriver -> {
WebElement receipt = currentDriver.findElement(RECEIPT);
return receipt.getText().isBlank() ? null : receipt;
});
}
}The extra stale-element rule is appropriate only if this receipt is expected to rerender. If the DOM contract says the node remains stable, remove it. The immediate exception then exposes a product regression or page-object defect instead of extending the test.
These three examples share a visible TimeoutException only when the policy is too broad. Their fixes are deliberately different: relocate an expected replacement, preserve state history around null returns, and fail fast when the driver cannot recover.
How to separate this problem from the near-misses
A timeout cause is evidence about the final evaluation, not proof that the wait policy is wrong. Before editing an ignore list, compare it with the condition ledger, browser state, and page contract.
A wrong frame is the classic near-miss. The payment button is visible in a screenshot, but findElement keeps throwing NoSuchElementException because WebDriver is searching the top document. WebDriverWait ignores that exception by default and eventually times out with it as the cause. Increasing the timeout or adding another ignored exception cannot change the browsing context.
Inspect the DOM hierarchy or the application markup for an iframe around the target. Then make the frame transition explicit. Selenium's frameToBeAvailableAndSwitchToIt condition both waits for the frame and switches the driver's context on success, so cleanup must return to the default content.
package example.checkout;
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
public final class HostedPaymentFrame {
private static final By FRAME =
By.cssSelector("iframe[data-testid='hosted-payment']");
private static final By PAY_BUTTON = By.id("pay-now");
private HostedPaymentFrame() {}
public static void clickPayButton(WebDriver driver) {
driver.switchTo().defaultContent();
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(FRAME));
try {
WebElement payButton = wait.until(
ExpectedConditions.elementToBeClickable(PAY_BUTTON));
payButton.click();
} finally {
driver.switchTo().defaultContent();
}
}
}This fix has a state-management cost. The helper must restore default content even when the visibility check or click fails. It waits for an interactable button, performs the click once outside the polling condition, and then restores context. Returning the frame-owned WebElement after switching back would leak a reference that callers cannot safely use. If a caller needs an observation instead of an action, return text or another domain value collected while the frame is active.
Mixed implicit and explicit waits create another near-miss. Selenium's waiting documentation warns against combining them because the resulting duration can be unpredictable. If findElement has a ten-second implicit wait, each explicit-wait condition evaluation may spend substantial time inside that locator before FluentWait checks its own clock. The stack trace still ends in TimeoutException and may still carry NoSuchElementException, but the wall time greatly exceeds what a reviewer expects from the explicit configuration.
The poll recorder distinguishes this case. Look at elapsed time between attempt records and the time spent inside each condition call. If there are very few evaluations and each locator blocks, inspect the session's implicit-wait setup before changing pollingEvery. The repair is usually to standardize on explicit waits and set the implicit timeout to zero at driver creation. That choice makes every unwrapped findElement fail immediately, so rollout must first locate code that relied on the global delay.
A third near-miss is a true product timeout. The element exists throughout, the condition repeatedly observes "Authorizing," and no Selenium exception occurs. A null timeout cause is correct. Check the application's network or server evidence using whatever logging the product already exposes, tied to the same test account and request. The browser wait should not manufacture an exception to make the report look more technical.
A fourth is an invalid condition. A selector typo normally produces an unignored selector exception immediately. A predicate can also throw an assertion or parsing exception that has nothing to do with page readiness. If a broad RuntimeException rule turns either into a timeout, remove the broad rule first. Do not add another poll recorder until the original exception can propagate; its stack frame is already the best evidence.
Use a simple triage order:
- Read the top exception and direct cause without assuming either is the root problem.
- Check whether the final poll returned null or false, threw an ignored type, or threw a type that should have propagated.
- Compare earlier polls for a state transition, repeated exception, or long condition execution.
- Verify browsing context, locator contract, and driver ownership.
- Compare the visible application state with the condition's exact success rule.
- Change only the boundary contradicted by evidence.
This order prevents an attractive but weak fix: adding StaleElementReferenceException because the final cause happened to be stale. If earlier polls show a stable "Declined" status and only the last poll overlaps teardown, the wait target or test lifecycle is more likely wrong.
How to roll a narrow policy through an existing suite
Do not replace a broad ignore rule everywhere in one blind edit. Some tests may rely on it for legitimate late-mounted elements, while others have been hiding defects for months. Start with an inventory of constructors, ignoring calls, and shared wait helpers. The following shell commands are read-only and work from a Maven repository root when ripgrep is installed.
set -eu
rg -n --glob '*.java' 'new (FluentWait|WebDriverWait)' src || true
rg -n --glob '*.java' '\.(ignoring|ignoreAll)\(' src || true
rg -n --glob '*.java' \
'(Exception|RuntimeException|WebDriverException)\.class' src || trueClassify each match by the operation inside until. A locator-only observation is different from an action such as click or sendKeys. Polling an observation is usually idempotent. Repeating an action can submit a form twice, send duplicate text, or accept a dialog the test did not intend to accept. Move actions outside the wait unless the action's repeat behavior is explicitly safe and tested.
Next, record the current behavior before narrowing anything. Use actual CI results, not invented poll counts or timing targets. Capture which ignored classes occur, how often the final cause is null, how long each condition call takes, and whether retries replace failed artifacts. Keep the baseline long enough to include the browsers and Grid paths the suite officially supports.
Introduce the recorder at one shared helper boundary, but emit its ledger only when the wait fails. Successful waits can generate large volumes of low-value logs, especially with short intervals. Failure-only output reduces storage, though the list still occupies memory until the wait ends. Cap or sample records if a suite uses long timeouts and very frequent polling, and always keep the final few evaluations.
For each broad rule, write a focused test that proves the intended recovery. A stale-node test should replace the node and demonstrate that relocating it can succeed. A late-mount test should start without the node and add it before the condition completes. A dead-session test should verify immediate propagation rather than timeout wrapping. These are framework contract tests, not retries around production end-to-end tests.
Then change helpers in small groups. Begin with pages whose render transitions are understood. Remove parent exception classes, add the specific recoverable type where needed, and keep a named message for the business condition. Watch for newly exposed failures. An immediate invalid-selector or session error is a successful reclassification, not necessarily a regression in the new helper.
A CI job should retain the first failed attempt's Surefire output even when the repository permits a later retry. This GitHub Actions example runs targeted contract tests and uploads their XML and text reports on success or failure. Replace the test class names with the names used by the repository.
name: wait-contracts
on:
pull_request:
workflow_dispatch:
jobs:
selenium-wait-contracts:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"
cache: maven
- name: Run wait contract tests
run: >
mvn --batch-mode --no-transfer-progress
-Dtest=PaymentStatusWaitTest,PollRecorderTest test
- name: Retain wait diagnostics
if: always()
uses: actions/upload-artifact@v4
with:
name: wait-contract-reports
path: target/surefire-reports/
if-no-files-found: errorArtifact upload is not the diagnostic by itself. Confirm that the test runner writes standard output or a structured attachment into the retained path. If logging is redirected elsewhere, add that existing log directory explicitly. Do not upload a whole workspace merely to capture one wait ledger; browser profiles and downloaded fixtures may contain sensitive test data.
Make ownership clear during rollout. The page object owns selectors and browsing context. The wait helper owns timeout, interval, ignore policy, and poll evidence. The test owns the business expectation. The driver fixture owns session creation and teardown. When all four live in one static utility, a broad catch often hides which layer broke.
A review checklist for each migrated wait should answer these concrete questions:
- Can the ignored exception become false on the next evaluation without rebuilding the test setup?
- Does the condition locate fresh elements when replacement is allowed?
- Can null or false erase an earlier cause, and is separate state evidence available?
- Will an unignored session, selector, assertion, or parsing failure propagate immediately?
- Does the timeout message name the user-visible state rather than the helper method?
- Are failed-attempt diagnostics retained under a unique test and attempt identity?
- Is the condition observational, or could polling repeat a destructive action?
- Are implicit waits disabled or deliberately accounted for?
After the first migration group is stable, expand by page family. Delete temporary verbose logging only after the structured failure artifact has proved sufficient. Keep the contract tests. They protect the semantic difference between "retry this page race" and "hide every runtime failure."
What the fix costs, and when to stop waiting
Narrow exception policies improve classification, but they are not free. Every ignored exception spends part of the timeout budget. If a NoSuchElementException comes from a misspelled but syntactically valid locator, WebDriverWait's default policy may wait until timeout before exposing it. A clear condition message and locator contract test reduce that cost; they do not eliminate it.
Relocating on every poll adds browser commands. On a remote Grid, each command crosses the network. A 100 millisecond polling interval does not guarantee ten clean evaluations per second, and aggressive polling across hundreds of parallel sessions can add load without making the application ready sooner. Choose an interval based on the transition being observed and measure it in the real execution environment.
Recording every attempt adds memory, log volume, and redaction work. It can also alter timing slightly, particularly if each poll writes synchronously to a remote logger. Store lightweight records in memory and print once on failure. If timing is the suspected bug, run a comparison with the recorder disabled and label that comparison honestly.
A narrow stale-element retry can hide a weak selector. Relocating is safe only when the new match represents the same logical component. A selector that matches the first row in a changing table may silently move to another order after rerender. Prefer a key tied to the domain object, then assert the identity before accepting the desired state.
Do not ignore an exception when the next poll has no route to recovery. Closed sessions, missing windows that will not be reopened, invalid selectors, type errors, assertion failures, data parsing defects, and fixture setup errors belong in that category. Let them fail at the line that produced them. When WebDriverWait's default NotFoundException parent rule is too broad for a condition, construct a plain FluentWait and state the recoverable types explicitly.
Do not wrap business actions in a wait simply because they sometimes fail. A payment click, order submission, or file upload can have side effects even when WebDriver reports an exception. Separate the action from the observation: perform the action once, then poll for a receipt, state transition, or other idempotent result.
Do not use a browser wait to cover an unknown backend service-level objective. If the product permits a long asynchronous process, choose a timeout from the product contract and expose progress that a user can observe. If no contract exists, increasing a UI timeout only converts uncertainty into suite latency.
Do not infer history from TimeoutException.getCause(). A non-null cause tells you what the final evaluation threw and Selenium ignored. A null cause tells you the final evaluation did not leave an ignored exception. Neither statement describes all prior polls. Add explicit instrumentation when chronology changes the diagnosis.
Finally, do not add ignored exceptions to make a flaky test green before identifying the transition. A justified rule names the temporary state, the exception it can produce, the future poll that can succeed, and the evidence retained when it does not. If those four details are unavailable, allow the original failure to surface and investigate it at its real boundary.
// FIELD DISPATCH
Get the QA Field Notes
Weekly QA battles, AI testing guides, and interview drills. Free on Substack.
// LIVE COURSE / THE TESTING ACADEMY
Playwright Automation Mastery
Go beyond Selenium. Master Playwright with JS/TS in 90 days.
From the instructor behind this guide.
Playwright jobs are growing 8x faster than Selenium. 90 days / 75+ live hrs / Tue-Thu-Sat 7 AM IST.
PRIMARY REFERENCES
Verify the details at the source
QABattle guides are practical explanations. Product behavior, standards, and APIs can change, so use these primary references for the canonical details.
- 01Official selenium.dev reference
selenium.dev
Primary documentation selected and verified for the claims in this guide.
- 02Official selenium.dev reference
selenium.dev
Primary documentation selected and verified for the claims in this guide.
- 03Official selenium.dev reference
selenium.dev
Primary documentation selected and verified for the claims in this guide.
- 04Official selenium.dev reference
selenium.dev
Primary documentation selected and verified for the claims in this guide.
FAQ / QUICK ANSWERS
Questions testers ask
Why is TimeoutException.getCause() null after an ignored exception?
A later poll probably returned null or false. Selenium clears its remembered exception after such a poll, so an earlier ignored exception is not attached when the wait eventually times out.
Which exceptions does Java WebDriverWait ignore by default?
Java's WebDriverWait ignores NotFoundException and its subclasses during the condition. Other exceptions propagate immediately unless the test adds their type to the ignore list.
Should I ignore StaleElementReferenceException in every explicit wait?
Only add it when DOM replacement is an expected part of the state transition and the condition finds the element again on every poll. A stale reference caused by a cached page object or a wrong lifecycle needs a code fix, not more retries.
How can I see every exception thrown during a FluentWait?
Wrap the condition with a small recorder that logs each return value or exception before rethrowing it. Keep that history in the test framework because FluentWait retains at most one cause for timeout construction.
Why is ignoring WebDriverException a bad wait policy?
That parent type covers failures the page cannot heal, including broken sessions and transport problems. Retrying them delays classification and converts a useful immediate error into a less specific timeout.
RELATED GUIDES
Continue the learning route
GUIDE 01
Java Exception Design for Actionable Automation Failures
Java exception design automation failures: practical design, implementation, debugging, CI, metrics, and interview guidance for QA, SDET, and automation engineers.
GUIDE 02
Selenium Java Exception Handling Interview Questions for SDETs
Selenium Java Exception Handling: practical interview scenarios, model-answer guidance, scoring criteria, common mistakes, and a focused readiness checklist.
GUIDE 03
A 60-Day SDET Coding Interview Roadmap for Java Beginners
SDET Coding Interview Roadmap for Java Beginners interview guide with realistic scenarios, model-answer guidance, scoring, common mistakes, and practical.
GUIDE 04
Add Selenium Accessibility Testing to User Journeys
Master Selenium accessibility testing with practical examples, architecture decisions, failure analysis, CI guidance, metrics, and scenario-led interview answers.
GUIDE 05
Advanced Java Automation Framework Interview Questions
advanced Java automation interview questions: practical design, implementation, debugging, CI, metrics, and interview guidance for QA, SDET, and automation engineers.