skip to content

In Selenium's Java WebDriverWait, which values returned by a custom condition keep it polling?

level: middleimportance: should knowfreq 58%

answer

  1. Two values keep the loop alive
  2. The false test is type-aware
  3. An empty list is still a result
  4. findElements never returns null
  5. Return null yourself when not ready

basics

~20 s

Only two: null, and a Boolean whose value is false. Every other result ends the wait and is returned by until, including an empty list, an empty string and a zero, which is why an empty findElements result never waits.

solid answer

~40 s

In Selenium 4 the Java loop keeps going only while the condition returns `null` or a `Boolean` that is `false`. Anything else is treated as the answer, returned from `until`, and ends the wait — and the `false` test is type-aware, so it applies to a `Boolean` and to nothing else. That is why returning `driver.findElements(...)` straight out of a condition never waits: an empty `List` is neither `null` nor a false `Boolean`, so the first poll succeeds with zero rows and the failure surfaces later as an index or size error. The same applies to an empty `String` from `getText()`. Decide emptiness yourself and return `null` while the page is not ready. Selenium's Python binding differs: its `until` uses Python truthiness, so there an empty list does keep polling.

code

java · 6 lines
java
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));

List<WebElement> rows = wait.until(d -> {
  List<WebElement> found = d.findElements(By.cssSelector("#nursery-order tbody tr"));
  return found.isEmpty() ? null : found;
});

go deeper

for a junior

Learn the two stop-the-loop exceptions: null and false keep waiting, everything else counts as the answer. When in doubt, return null explicitly for the not-ready case rather than relying on emptiness.

for a middle

Be able to state the rule precisely, including that the false check only applies to a Boolean. Then explain from that rule why a condition returning findElements ends on the first poll.

for a senior

Show how you diagnose it in a suite: a wait that never waits, and an index or size failure a few lines later. Point at the condition's return type as the first thing you inspect.

for a principal

Frame it as an API shape the team relies on. Decide whether shared wait helpers should ever return collections, and what convention keeps not-ready expressed as null everywhere in the codebase.

## The exact rule Selenium applies `FluentWait.until` — the loop behind `WebDriverWait` — does not ask whether your value is "truthy" in any loose sense. In the Java bindings it applies one precise test to whatever your condition returned: - the value is **not `null`**, and - the value is **not the `Boolean` `false`**. Everything that passes both checks ends the wait and is handed back as the return value of `until`. In Selenium 4 that means exactly two results keep the loop running: `null`, and a `Boolean` whose value is `false`. Nothing else does. A `Boolean.TRUE` stops it, and so does every non-`Boolean` object, whatever it contains. That is stricter than most people's mental model, because the rule is **type-aware**: the `false` check applies only when the returned object's class is `Boolean`. A `String` that happens to be empty, a `List` that happens to have no members, and a `Long` that happens to be `0` are all "not null and not a false Boolean", so all three **end the wait**. ## Values that surprise people | Your condition returns | Java `WebDriverWait` | Python `WebDriverWait` | |---|---|---| | `null` / `None` | keeps polling | keeps polling | | `false` / `False` | keeps polling | keeps polling | | `""` (empty string) | **stops and returns it** | keeps polling | | `List` with no elements | **stops and returns it** | keeps polling | | `Long` `0` from a script | **stops and returns it** | keeps polling | | a `WebElement` | stops and returns it | stops and returns it | | `true` / `True` | stops and returns it | stops and returns it | The right-hand column is the cross-binding footnote worth knowing: Selenium's Python `WebDriverWait.until` returns the value when `if value:` is satisfied, so it follows **Python truthiness** and an empty list, an empty string or `0` all keep it polling. The Java rule is narrower. If you move a condition between the two bindings, the empty-collection row is where the behaviour silently diverges. ## Why findElements is the classic trap The most common way to write a custom condition on a **plant-nursery order form** is to look for the order lines and wait until some exist: ```java List<WebElement> rows = new WebDriverWait(driver, Duration.ofSeconds(10)) .until(d -> d.findElements(By.cssSelector("#nursery-order tbody tr"))); ``` This never waits. `findElements` returns an **empty list** when nothing matches — it does not throw and it does not return `null` — and an empty `ArrayList` is neither `null` nor a `false` `Boolean`, so the very first poll succeeds and `rows` comes back with zero elements. The failure then surfaces somewhere else entirely, usually as an `IndexOutOfBoundsException` or an assertion on a size of zero, several lines below the wait that was supposed to prevent it. The fix is to make the "not ready" case return `null` explicitly: 1. Do the read: `List<WebElement> found = d.findElements(...)`. 2. Decide readiness in your own code: is `found.isEmpty()`, or is the count below what you expect? 3. Return `null` when it is not ready, and the list itself when it is. The same pattern applies to text. `getText()` on an empty cell gives `""`, which stops the wait, so a condition waiting for the subtotal to be populated must return `null` while the text is blank or still a placeholder, and only return the string once it actually carries a value. ## Choosing what to return - Return the **thing you need next** — the `WebElement`, the `List<WebElement>`, the `String` — so `until` hands it straight to the following line and you do not re-find it. - Return `null`, not `false`, when the useful result is an object; mixing `null` and `false` in one condition makes the return type awkward and gains nothing. - Return `Boolean` only for genuine yes/no checks, where the value of `until` is not interesting. - Never return a **collection or a string** without deciding emptiness yourself; the wait will not decide it for you. - Remember that `until` is declared to return a non-null value, so a condition that can only ever produce `null` will always end in `TimeoutException`. ## Reading a failure back to this rule When a custom wait "does not wait", check the return type first. A condition typed to return `List`, `String`, `Map` or a boxed number is nearly always the cause, because those are the types with a natural empty value that Java's rule still counts as a result. A condition typed to return `WebElement` or `Boolean` is far harder to get wrong: the element case is either found or throws, and the boolean case matches the rule exactly.

  • Your condition returns getText() on the subtotal cell and the wait ends instantly. What is happening?
    `getText()` on a cell that has not been populated yet returns an empty string, and an empty `String` is neither `null` nor a false `Boolean`, so the first poll counts as success. Compare the text against what you actually expect and return `null` while it is blank or still a placeholder, returning the string only once it carries a real value.
  • Does returning Boolean.FALSE differ from returning null in how the wait behaves?
    Not in behaviour: both keep the loop running until the timeout. They differ in what `until` can give you back. A boolean condition can only ever return `true`, which is useless as a value, whereas returning the element or the text you were waiting for lets the next line use the result directly instead of finding it again.
  • Can a condition legitimately return zero as a successful result?
    Yes, and Java will accept it, because a boxed `0` is neither `null` nor a false `Boolean`. If you are waiting for a count to fall to zero — no pending nursery order lines left — returning the number works. Just be aware the same rule is what makes an accidental empty list look like success.

saying these in an interview costs you the question

  • Thinks an empty list from findElements keeps the wait polling
  • Believes any falsy value ends the polling loop in Java
  • Expects findElements to return null when nothing matches
  • Assumes Java and Python bindings judge results identically
  • Returns getText output without checking it is non-blank