Back to Blog
Technology
January 8, 2026
4 min read
693 words

Why We Banned 'Implicit Waits'. The Root Cause of Flaky Tests.

The single most dangerous line of code in Selenium is `driver.manage().timeouts().implicitlyWait(10)`. It creates 'Ghost Waits', conflates errors, and destroys determinism. Here is why we strictly use Fluent Waits.

Why We Banned 'Implicit Waits'. The Root Cause of Flaky Tests.

If I audit a Selenium codebase and I see this line of code, I fail the audit immediately:

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

This single line is responsible for 80% of "Flaky Tests" in the ecosystem. It is the "Global Variable" of automation.

At XQA, we issue a strict ban: Implicit Wait time must be 0. Always.

Here is the technical deep dive into why Implicit Waits broken, the concept of "Ghost Waits," and how to implement a robust Fluent Wait strategy instead.

The "Ghost Wait" Phenomenon

What happens when you mix Implicit Waits and Explicit Waits?

Documentation says: "Don't mix them." But it doesn't say why. Let's look at the driver internals.

Imagine you have an Implicit Wait of 10 seconds. You also have an Explicit Wait (WebDriverWait) of 15 seconds checking for an element to be clickable.

Technically, the Explicit Wait polls the DOM every 500ms. It asks the driver: "Is element X present?"

The Trap:

  1. Explicit Wait calls findElement.
  2. Element is not there yet.
  3. The Driver sees the Implicit Wait is set to 10s. So the findElement call blocks for 10 seconds before returning "False".
  4. The Explicit Wait receives the "False" result after 10 seconds.
  5. The Explicit Wait sleeps for 500ms and tries again.

Suddenly, your "15 second" timeout might actually take 25 seconds, or 40 seconds, depending on the phase alignment.

Even worse, if the element appears at second 11, the driver might still be blocked in a command initiated at second 1. This creates Unpredictable Latency.

Section 1: The Conflation of Errors

Implicit Waits only check for one thing: Presence in the DOM.

They do not check:

  • Is the element visible? (display: none?)
  • Is the element enabled?
  • Is the element obscured by a spinner?

If you rely on Implicit Waits, your script assumes "Found = Ready." This is false in modern SPAs.

React might render the <button> in the DOM, but it is effectively dead until the hydration logic finishes 50ms later. Your Implicit Wait returns immediately (Success!), your script tries to .click(), and it fails with ElementNotInteractableException.

You have traded a "NoSuchElement" error for a random runtime crash.

Section 2: Fluent Waits (The Only Correct Way)

We replaced all Implicit Waits with Fluent Waits (or Explicit Waits).

The logic is: "Listen, do not wait."


// The Robust Pattern
const button = await driver.wait(
    until.elementIsVisible(driver.findElement(By.id('submit'))), 
    10000, 
    'Submit button never appeared'
);
    

But writing this every time is tedious. This is why people fallback to Implicit Waits. It's lazy.

The Wrapper Solution:

We built a wrapper: SmartDriver.find(locator).

This wrapper automatically applies a strict Fluent Wait conditions:

  1. Wait for Presence.
  2. Wait for Visibility.
  3. Wait for Enablement.
  4. Wait for Stability (Rect not moving).

This ensures that we never interact with an element unless it is truly ready for a user.

Section 3: Handling the "StaleElementReferenceException"

The other nemesis of Selenium is the Stale Element.

This happens when the JS framework destroys and recreates the DOM node between the time you found it and the time you checked it.

Implicit Waits make this worse because they tempt you to store the element reference.

The Fix: "Find Just in Time" (JIT).

Our framework never stores WebElement objects in variables. We pass Locators (Selectors) around.

Code Example:


// BAD
const btn = driver.findElement(By.id('btn'));
// ... do other stuff ...
btn.click(); // FAIL: Stale Element


// GOOD
await Click.on(By.id('btn')); // The internal logic finds the element at the exact moment of action.
    

Section 4: Network Interception for Determinism

The ultimate evolution beyond waiting is Network Synchronization.

Instead of waiting for a UI element (which is a side effect), wait for the Network Call (the cause).

await driver.wait(network.response('api/login'))

If you know the data has arrived, you theoretically don't need to wait for the UI spin. (Though in practice, you should do both).

Selenium 4's BiDi (Bi-Directional) APIs and CDP (Chrome DevTools Protocol) make this easier. We now have event-driven synchronization.

Conclusion

Implicit Waits were a mistake in the Selenium API design. Even the project maintainers admit it.

They are a crutch for bad synchronization logic.

Ban them. Set the timeout to 0. Force your team to think about what they are waiting for, not just how long.

Tags:TechnologyTutorialGuide
X

Written by XQA Team

Our team of experts delivers insights on technology, business, and design. We are dedicated to helping you build better products and scale your business.