skip to content

In Selenium, why can a click throw ElementClickInterceptedException on the line right after elementToBeClickable passed?

level: seniorimportance: must knowfreq 61%

answer

  1. Two questions, asked at two moments
  2. The predicate reads only the target
  3. Who checks occlusion, and when
  4. Nothing is atomic between until and click
  5. Scope problem first, timing problem second

basics

~20 s

Because the two ask different questions at different moments. The wait only confirmed displayed and enabled; obstruction is checked by the browser when the click command runs, and the page is free to change in between.

solid answer

~40 s

There are two separate reasons, and the first matters more. `elementToBeClickable` evaluates only `isDisplayed()` and `isEnabled()` on the target, so an element that is rendered and not disabled satisfies it even when something is drawn completely over it — obstruction is detected by the browser while executing the click, and surfaces as `ElementClickInterceptedException`. Second, `until` returns on the poll where the condition first held, and your `click()` is a separate round trip sent afterwards; the document can change in that window. On a cinema showtime picker, the `19:45` button sits under a “checking seat availability” panel: displayed, enabled, and unclickable. A longer timeout cannot help, because the wait did not time out — it succeeded early, on the wrong question.

code

java · 23 lines
java
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.ElementClickInterceptedException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class ShowtimeInterception {

  public static void pickScreening(WebDriver driver) {
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));

    wait.until(ExpectedConditions.invisibilityOfElementLocated(
        By.cssSelector(".availability-check")));

    try {
      wait.until(ExpectedConditions.elementToBeClickable(
          By.cssSelector(".showtime[data-time='19:45']"))).click();
    } catch (ElementClickInterceptedException e) {
      throw new AssertionError(e.getMessage(), e);
    }
  }
}

go deeper

for a junior

Remember the headline: a wait passing does not mean the next command will work. Being able to say that the condition checked only displayed and enabled is enough at this stage.

for a middle

Explain the split. The condition evaluates two booleans on the target in your process; obstruction is decided by the browser as part of the click command. Then explain why time passing between them matters too.

for a senior

An interviewer expects diagnosis, not a workaround. Show that you separate the scope problem from the timing problem, that you can pick a predicate whose truth implies the action succeeds, and that you know a longer timeout is not the answer.

for a principal

Own the standard that a wait must be false while the action would fail. Framing synchronisation as a contract about the action rather than about an element is the tradeoff a lead sets, and it decides how brittle a suite becomes.

## Two different questions, asked at two different moments A passing wait followed by a failing click is not a bug in either one. They ask different questions, and they ask them at different times. **The wait's question**, asked by `ExpectedConditions.elementToBeClickable` on each poll, is: *is this element displayed, and is it enabled?* That is the entire predicate — a `findElement`, an `isDisplayed()`, an `isEnabled()`. When both booleans are true the condition returns the element and `until` ends. **The click's question**, asked by the browser when the Element Click command executes, includes something the predicate never looked at: *is the target actually reachable at the point the pointer would land, or is something else in the way?* When the answer is "something else is in the way", the remote end returns an `element click intercepted` error, which the Java binding raises as `ElementClickInterceptedException` — a subclass of `ElementNotInteractableException`. So there are two independent reasons the pair can disagree: 1. **The predicate never checked obstruction at all.** Even if the page were completely frozen, a covered-but-displayed button satisfies the wait and fails the click. This one is not a timing problem; it is a scope problem. 2. **Time passed between the two.** `until` returns on the poll where the condition first held. Your `click()` runs after that — after the return, after any assertion or logging in between, after the round trip to the remote end. The document is free to change in that window. ## What the two operations look like side by side | | `elementToBeClickable` | `WebElement.click()` | |---|---|---| | Who evaluates it | the client-side condition, in your test process | the browser, via the remote end | | Reads element state | `isDisplayed()`, `isEnabled()` | the same, plus whether the target is obscured | | Considers other elements | never | yes — that is where interception is detected | | On failure | returns `null`, poll again, eventually `TimeoutException` | throws `ElementClickInterceptedException` immediately | | Repeats itself | yes, until the timeout | no — one attempt, one verdict | ## The cinema showtime picker version Choosing a screening date on a showtimes page fires a request for that date's seat counts. While it is in flight the app draws a "checking seat availability" panel across the grid; when it resolves, the panel is removed and each showtime button gets its remaining-seats badge. - If your wait runs while the panel is up, the `19:45` button underneath is displayed and enabled, so the wait returns instantly and the click lands on the panel: `ElementClickInterceptedException`. - If your wait runs after the panel is gone but the badge render replaces the grid a moment later, the wait returns a reference to a node that is about to be detached, and the click fails with `StaleElementReferenceException` instead. - Both failures come from the same root: **the wait was asked about the button, and the problem was the page**. ## Why the gap cannot be closed by waiting harder - WebDriver has **no atomic wait-then-act primitive**. `until` returns a value to your code; the command you send next is a separate round trip. There is always a window, and its width is not something you configure. - Increasing the timeout does not help, because the wait did not time out — it succeeded, early, on a condition that was never the right one. - Adding a sleep after the wait narrows the window by luck rather than closing it, and turns a fast test into a slow one that still fails sometimes. - The honest fix is to **make the predicate assert the thing you actually depend on**: wait for the availability panel to be gone, or for the badge that only exists after the data arrives, rather than for a button that was displayed and enabled the whole time. Selenium ships conditions for the absence of an element for exactly this reason. - Where interception is genuinely unavoidable, the recovery — scrolling, dismissing the blocker, retrying the action — is a separate design decision from the wait, and it should not be smuggled into the wait predicate. ## What a good answer sounds like in an interview Say plainly that `elementToBeClickable` encodes `isDisplayed()` plus `isEnabled()` and nothing about occlusion; that obstruction is detected by the browser at click time, not by the condition; and that the interval between `until` returning and the command being sent is real and unbounded by anything you control. Then say what you would change: pick a predicate whose truth actually implies the action can succeed, usually a signal about the blocking thing rather than about the target.

  • Would raising the WebDriverWait timeout make this failure less likely?
    No, and reaching for it is a tell. The wait did not run out of time — it succeeded, usually on its first poll, because the target really was displayed and enabled. A timeout bounds how long a false condition may stay false; it does nothing when the condition is already true and simply is not the condition you needed.
  • How would you change the wait so its truth actually implies the click can land?
    Synchronise on the thing that is in the way rather than on the target. Wait for the availability panel to be gone, or for a marker the app only renders once the seat data has arrived, and then wait for the showtime button. The predicate should be false for as long as the action would fail.
  • Why can the same sequence fail with StaleElementReferenceException instead?
    Because the other thing that can change in the gap is the element itself. If the grid re-renders between `until` returning and the click being sent, the reference you were handed points at a detached node, and the command fails on the reference rather than on occlusion. Same root cause, different symptom.

It is like reading the free-seats board in the cinema lobby and then walking in: the board was telling the truth when you read it, and it never claimed to know who would be standing in the aisle by the time you got there.

saying these in an interview costs you the question

  • Blames a too-short timeout for an intercepted click
  • Adds a fixed sleep after the wait and calls it fixed
  • Says elementToBeClickable already waits out overlays
  • Assumes until and click execute as one atomic step
  • Treats the exception as a Selenium bug rather than a report