Skip to content
QA Knowledge Hub

StaleElementReferenceException in Selenium

Updated 2026-11-12 · 3 min read

You are holding a reference to an element that the page has since removed or replaced. The element on screen looks the same, but it is a new one. Look the element up again, closer to when you use it.

org.openqa.selenium.StaleElementReferenceException: stale element reference:
element is not attached to the page document

A WebElement in Selenium is not a description of an element; it is a handle to one specific node in the DOM at the moment you found it. If that node is removed or replaced, the handle is dead, even if an identical-looking element is now on the page.

When it happens

  • You find a list of rows, click a row, the page re-renders the table, and you use the old row reference.
  • You find a button, an AJAX call refreshes the form, and the stored button is now a different node.
  • You navigate (click on a link, back) and use an element from the previous page.
  • A framework (React, Angular, Vue) re-creates elements during a state change.

Fix 1: find the element again right before using it

The best fix is usually to stop storing elements.

// Fragile: stored reference
WebElement save = driver.findElement(By.id("save"));
fillForm();            // causes a re-render
save.click();          // StaleElementReferenceException

// Better: look up at the moment of use
fillForm();
driver.findElement(By.id("save")).click();

In page objects, keep the By locator as a field and call findElement inside the method, not in the constructor.

Fix 2: wait for the element to be ready after the change

Wait for the new element, and then use what the wait returns.

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.elementToBeClickable(By.id("save"))).click();

Passing a By to ExpectedConditions makes it look the element up on each poll, so a re-render in the middle does not matter.

Fix 3: wait for the old element to go away first

When you know a refresh is coming, wait for the old node to become stale, then find the new one.

WebElement oldRow = driver.findElement(By.cssSelector("tr.row"));
driver.findElement(By.id("refresh")).click();
wait.until(ExpectedConditions.stalenessOf(oldRow));
WebElement newRow = wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("tr.row")));

Fix 4: retry once, in one place

If a page keeps re-rendering and you cannot control it, wrap the action in a small helper rather than adding try/catch everywhere.

static void clickWithRetry(WebDriver driver, By by) {
    for (int attempt = 0; attempt < 3; attempt++) {
        try {
            driver.findElement(by).click();
            return;
        } catch (StaleElementReferenceException e) {
            if (attempt == 2) throw e;
        }
    }
}

Treat this as a last resort. If you need it often, find out why the page re-renders so much.

What not to do

  • Do not catch the exception and ignore it. The action did not happen.
  • Do not add Thread.sleep. It hides the race and slows every run.
  • Do not keep elements in static fields or in a list you reuse across steps.

Interview version

"A stale element reference means the DOM node I held was removed or replaced, usually by a re-render after a click or an AJAX update. I avoid it by not storing elements, by locating them again at the moment of use, and by waiting with a condition on a locator rather than on an element I already hold."

Quick answers

Why does the element look the same but still be stale?

When the page re-renders a part of the DOM, JavaScript often creates new elements with identical text and attributes. Your reference points to the old one, which has been detached, so Selenium throws.

Want a project you can show in interviews?

The portfolio project kit walks you through testing a deliberately buggy app with Selenium or Playwright and API tests, then explaining it. The app itself is free.