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 (
clickon 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.
