Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I am implementing a lot of Selenium tests using Java - sometimes, my tests fail due to a StaleElementReferenceException .

Could you suggest some approaches to making the tests more stable?

I recommend using this library, which solves the intermittent StaleElementReference exception and brings some enhancements to Selenium page Object model and Page Factory features: github.com/fslev/selenium-jutils#retry-on-error Slev Florin Jul 27, 2021 at 16:37 @SlevFlorin That library uses PageFactory which the Selenium creator that wrote it said not to use. JeffC Oct 10, 2021 at 20:59 In c# there is a method called StalenessOf(IWebElement element) inside the ExpectedConditions class which can be used. Don't know if the class also implements the method in java 3r1c Jun 1, 2022 at 13:57 @JeffC could you please provide more details about where and why creator said not to use it ? Some doc link ? The official doc is here: github.com/SeleniumHQ/selenium/wiki/PageFactory and there is no note refering to what you said Slev Florin Oct 13, 2022 at 18:48 @SlevFlorin This is a video of Simon Stewart, the creator of WebDriver, Selenium Project lead, and creator of PageFactory at seleniumconf a few years ago stating not to use PageFactory. youtu.be/gyfUpOysIF8?t=1518 . I started the section a little early to give context but at 27:27 he specifically states, "Don't use page factory." JeffC Oct 14, 2022 at 14:07

This can happen if a DOM operation happening on the page is temporarily causing the element to be inaccessible. To allow for those cases, you can try to access the element several times in a loop before finally throwing an exception.

Try this excellent solution from darrelgrainger.blogspot.com :

public boolean retryingFindClick(By by) {
    boolean result = false;
    int attempts = 0;
    while(attempts < 2) {
        try {
            driver.findElement(by).click();
            result = true;
            break;
        } catch(StaleElementException e) {
        attempts++;
    return result;
                If the above doesn't solve it, updating to the latest chromedriver is what solved it for us.
– Vdex
                Feb 7, 2020 at 11:03
                @jspcal Should we be doing this for every element we try to click since this reloading/StaleElement Exception can potentially happen anytime?
– tonitone120
                Feb 27 at 1:28

I was having this issue intermittently. Unbeknownst to me, BackboneJS was running on the page and replacing the element I was trying to click. My code looked like this.

driver.findElement(By.id("checkoutLink")).click();

Which is of course functionally the same as this.

WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
checkoutLink.click();

What would occasionally happen was the javascript would replace the checkoutLink element in between finding and clicking it, ie.

WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
// javascript replaces checkoutLink
checkoutLink.click();

Which rightfully led to a StaleElementReferenceException when trying to click the link. I couldn't find any reliable way to tell WebDriver to wait until the javascript had finished running, so here's how I eventually solved it.

new WebDriverWait(driver, timeout)
    .ignoring(StaleElementReferenceException.class)
    .until(new Predicate<WebDriver>() {
        @Override
        public boolean apply(@Nullable WebDriver driver) {
            driver.findElement(By.id("checkoutLink")).click();
            return true;

This code will continually try to click the link, ignoring StaleElementReferenceExceptions until either the click succeeds or the timeout is reached. I like this solution because it saves you having to write any retry logic, and uses only the built-in constructs of WebDriver.

Kenny's solution is good, however it can be written in a more elegant way

new WebDriverWait(driver, timeout)
        .ignoring(StaleElementReferenceException.class)
        .until((WebDriver d) -> {
            d.findElement(By.id("checkoutLink")).click();
            return true;

Or also:

new WebDriverWait(driver, timeout).ignoring(StaleElementReferenceException.class).until(ExpectedConditions.elementToBeClickable(By.id("checkoutLink")));
driver.findElement(By.id("checkoutLink")).click();

But anyway, best solution is to rely on Selenide library, it handles this kind of things and more. (instead of element references it handles proxies so you never have to deal with stale elements, which can be quite difficult). Selenide

Your second solution would work because element goes to stale when you click it not when you find it. – Rajagopalan Mar 25, 2019 at 9:35 Use selenide to avoid this problem, much easier. Selenium is not meant to be used alone because of this issue and the fact that is a low level API for a simple user – cocorossello Mar 25, 2019 at 10:12 I am perfectly aware of that. I am using WATIR which is a wrapper around Ruby Selenium Binding, WATIR automatically takes care of all these problem(for an instance stale element). I am looking for something equivalent in Java binding, I found Selenide but I don't know how to change the implicit wait and explicit wait in selenide. Can you tell me how to do that? Or is there any material you can offer me where I can refer? and what's your opinion on FluentLenium? – Rajagopalan Mar 25, 2019 at 11:51 People should know that the selected answer by the OP dates back to 2012. MANY things have changed in the past 7 years. This answer is more correct for 2019. – hfontanez Apr 2, 2019 at 17:50

Generally this is due to the DOM being updated and you trying to access an updated/new element -- but the DOM's refreshed so it's an invalid reference you have..

Get around this by first using an explicit wait on the element to ensure the update is complete, then grab a fresh reference to the element again.

Here's some psuedo code to illustrate (Adapted from some C# code I use for EXACTLY this issue):

WebDriverWait wait = new WebDriverWait(browser, TimeSpan.FromSeconds(10));
IWebElement aRow = browser.FindElement(By.XPath(SOME XPATH HERE);
IWebElement editLink = aRow.FindElement(By.LinkText("Edit"));
//this Click causes an AJAX call
editLink.Click();
//must first wait for the call to complete
wait.Until(ExpectedConditions.ElementExists(By.XPath(SOME XPATH HERE));
//you've lost the reference to the row; you must grab it again.
aRow = browser.FindElement(By.XPath(SOME XPATH HERE);
//now proceed with asserts or other actions.

Hope this helps!

The reason why the StaleElementReferenceException occurs has been laid out already: updates to the DOM between finding and doing something with the element.

For the click-Problem I've recently used a solution like this:

public void clickOn(By locator, WebDriver driver, int timeout)
    final WebDriverWait wait = new WebDriverWait(driver, timeout);
    wait.until(ExpectedConditions.refreshed(
        ExpectedConditions.elementToBeClickable(locator)));
    driver.findElement(locator).click();

The crucial part is the "chaining" of Selenium's own ExpectedConditions via the ExpectedConditions.refreshed(). This actually waits and checks if the element in question has been refreshed during the specified timeout and additionally waits for the element to become clickable.

Have a look at the documentation for the refreshed method.

In my project I introduced a notion of StableWebElement. It is a wrapper for WebElement which is able to detect if element is Stale and find a new reference to the original element. I have added a helper methods to locating elements which return StableWebElement instead of WebElement and the problem with StaleElementReference disappeared.

public static IStableWebElement FindStableElement(this ISearchContext context, By by)
    var element = context.FindElement(by);
    return new StableWebElement(context, element, by, SearchApproachType.First);

The code in C# is available on my project's page but it could be easily ported to java https://github.com/cezarypiatek/Tellurium/blob/master/Src/MvcPages/SeleniumUtils/StableWebElement.cs

private WebDriverWait Wait { get; set; } public DriverHelper(string driverUrl, int timeoutInSeconds) Driver = new ChromeDriver(); Driver.Url = driverUrl; Wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(timeoutInSeconds)); internal bool ClickElement(string cssSelector) //Find the element IWebElement element = Wait.Until(d=>ExpectedConditions.ElementIsVisible(By.CssSelector(cssSelector)))(Driver); return Wait.Until(c => ClickElement(element, cssSelector)); private bool ClickElement(IWebElement element, string cssSelector) //Check if element is still included in the dom //If the element has changed a the OpenQA.Selenium.StaleElementReferenceException is thrown. bool isDisplayed = element.Displayed; element.Click(); return true; catch (StaleElementReferenceException) //wait until the element is visible again element = Wait.Until(d => ExpectedConditions.ElementIsVisible(By.CssSelector(cssSelector)))(Driver); return ClickElement(element, cssSelector); catch (Exception) return false;

Invocation:

        DriverHelper driverHelper = new DriverHelper("http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp", 10);
        driverHelper.ClickElement("input[value='csharp']:first-child");

Similarly can be used for Java.

Kenny's solution is deprecated use this, i'm using actions class to double click but you can do anything.

new FluentWait<>(driver).withTimeout(30, TimeUnit.SECONDS).pollingEvery(5, TimeUnit.SECONDS)
                    .ignoring(StaleElementReferenceException.class)
                    .until(new Function() {
                    @Override
                    public Object apply(Object arg0) {
                        WebElement e = driver.findelement(By.xpath(locatorKey));
                        Actions action = new Actions(driver);
                        action.moveToElement(e).doubleClick().perform();
                        return true;
                Ignoring the StaleElementReferenceException doesn't make it go away. This is just going to time out instead because once you get that exception, it doesn't just go away without some new action like refetching the element, etc.
– JeffC
                Dec 1, 2021 at 16:31
                By refetch I mean driver.findElement() again, store that element, and then do what needs to be done again. You might want to spend some time reading some articles on why StaleElementReferenceException is thrown, what a stale element is, and how to avoid it.
– JeffC
                Dec 7, 2021 at 15:02

I've found solution here. In my case element becomes inaccessible in case of leaving current window, tab or page and coming back again.

.ignoring(StaleElement...), .refreshed(...) and elementToBeClicable(...) did not help and I was getting exception on act.doubleClick(element).build().perform(); string.

Using function in my main test class:

openForm(someXpath);

My BaseTest function:

int defaultTime = 15;
boolean openForm(String myXpath) throws Exception {
    int count = 0;
    boolean clicked = false;
    while (count < 4 || !clicked) {
        try {
            WebElement element = getWebElClickable(myXpath,defaultTime);
            act.doubleClick(element).build().perform();
            clicked = true;
            print("Element have been clicked!");
            break;
        } catch (StaleElementReferenceException sere) {
            sere.toString();
            print("Trying to recover from: "+sere.getMessage());
            count=count+1;

My BaseClass function:

protected WebElement getWebElClickable(String xpath, int waitSeconds) {
        wait = new WebDriverWait(driver, waitSeconds);
        return wait.ignoring(StaleElementReferenceException.class).until(
                ExpectedConditions.refreshed(ExpectedConditions.elementToBeClickable(By.xpath(xpath))));

StaleElementReferenceException

StaleElementReferenceException indicates that the reference to an element is now stale i.e. the element no longer appears within the HTML DOM of the page.

Details

Every DOM Tree element is identified by the WebDriver by a unique identifying reference, known as a WebElement. The web element reference is a UUID used to execute commands targeting specific elements, such as getting an element's tag name or retrieving a property off an element.

When an element is no longer attached to the DOM, i.e. it has been removed from the document or the document has changed, it is said to be got stale. Staleness occurs for example when you have a web element reference and the document it was retrieved from navigates and due to navigation, all web element references to the previous document will be discarded along with the document. This will cause any subsequent interaction with the web element to fail with the stale element reference error.

Solution

The best approach to avoid StaleElementReferenceException is to induce WebDriverWait for the elementToBeClickable() before invoking click as follows:

new WebDriverWait(driver, Duration.ofSeconds(10)).until(ExpectedConditions.elementToBeClickable(By.cssSelector("elementCssSelector"))).click();

Note: You have to import the following:

import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.By;

Create a wrapper function (Java)

As an alternative to the accepted answer, my approach is similar in that it catches the exception and makes a few attempts, but it's more generic, so you can throw any kinds of actions at it as long as they are wrapped in a void function.

Please feel free to copy and use this code:

public void doPreventingStaleElement(Runnable function)
    int maxRetries = 3; // maximum number of retries 
    int retries = 0;
    boolean stale;
    // try/catch block attempts to fix a stale element
    do  {
        try {
            function.run();
            stale = false;
        catch (StaleElementReferenceException eStale) {
            stale = true;
            // Work-around for stale element reference when getting the first page
            if (retries < maxRetries) {
                retries++;
                System.out.println(function.getClass().getSimpleName() + " failed due to stale element reference, retry=" + retries);
                 try {
                     // Exponential increase of wait time - 1, 4, 9, 16, 25 seconds
                     Thread.sleep(1000 * (int) Math.pow(retries,2));
                 } catch (final InterruptedException e) {
                     Thread.currentThread().interrupt();
            else {
                System.out.println(function.getClass().getSimpleName() + " failed due to stale element reference, too many retries");
                eStale.printStackTrace();
                throw(eStale);
    } while (stale && retries < maxRetries);
    return;

Note that it will still throw a StaleElementReferenceException after maxRetries attempts.

Example of usage

As an example I want to do this:

final List<WebElement> buttons = getDriver().findElements(By.xpath("//button[@testid='dismiss-me']"));
for (final WebElement closeButton : buttons) {
    closeButton.click();

or this:

driver.findElement(By.id("login-form-username")).sendKeys(getUser());
driver.findElement(By.id("login-form-password")).sendKeys(getPassword());
driver.findElement(By.id("login-form-submit")).click();

Then I wrap them in void functions

private void clickButtons() {
    final List<WebElement> buttons = getDriver().findElements(By.xpath("//button[@testid='dismiss-me']"));
    for (final WebElement closeButton : buttons) {
        closeButton.click();
private void performLogin() {
    driver.findElement(By.id("login-form-username")).sendKeys(getUser());
    driver.findElement(By.id("login-form-password")).sendKeys(getPassword());
    driver.findElement(By.id("login-form-submit")).click();

and so I can just

doPreventingStaleElement(whateverObject::clickButtons);
doPreventingStaleElement(whateverObject::performLogin);

Clean findByAndroidId method that gracefully handles StaleElementReference.

This is heavily based off of jspcal's answer but I had to modify that answer to get it working cleanly with our setup and so I wanted to add it here in case it's helpful to others.

// This loops gracefully handles StateElementReference errors and retries up to 10 times. These can occur when an element, like a modal or notification, is no longer available.
export async function findByAndroidId( id, { assert = wd.asserters.isDisplayed, timeout = 10000, interval = 100 } = {} ) {
  MAX_ATTEMPTS = 10;
  let attempt = 0;
  while( attempt < MAX_ATTEMPTS ) {
    try {
      return await this.waitForElementById( `android:id/${ id }`, assert, timeout, interval );
    catch ( error ) {
      if ( error.message.includes( "StaleElementReference" ) )
        attempt++;
        throw error; // Re-throws the error so the test fails as normal if the assertion fails.
while (true) { // loops forever until break
    try { // checks code for exceptions
        WebElement ele=
        (WebElement)wait.until(ExpectedConditions.elementToBeClickable((By.xpath(Xpath))));  
        break; // if no exceptions breaks out of loop
    catch (org.openqa.selenium.StaleElementReferenceException e1) { 
        Thread.sleep(3000); // you can set your value here maybe 2 secs
        continue; // continues to loop if exception is found
                This code can very easily get into an infinite loop. You should just implement a counter or timeout to avoid this. Also, you don't need to hardcode a 3s sleep... that's wasted since the page isn't reloading so you can just try again immediately.
– JeffC
                Oct 10, 2021 at 21:05

There could be a potential problem that leads to the StaleElementReferenceException that no one mentioned so far (in regard to actions).

I explain it in Javascript, but it's the same in Java.

This won't work:

let actions = driver.actions({ bridge: true })
let a = await driver.findElement(By.css('#a'))
await actions.click(a).perform() // this leads to a DOM change, #b will be removed and added again to the DOM.
let b = await driver.findElement(By.css('#b'))
await actions.click(b).perform()

But instantiating the actions again will solve it:

let actions = driver.actions({ bridge: true })
let a = await driver.findElement(By.css('#a'))
await actions.click(a).perform()  // this leads to a DOM change, #b will be removed and added again to the DOM.
actions = driver.actions({ bridge: true }) // new
let b = await driver.findElement(By.css('#b'))
await actions.click(b).perform()

Usually StaleElementReferenceException when element we try to access has appeared but other elements may affect the position of element we are intrested in hence when we try to click or getText or try to do some action on WebElement we get exception which usually says element not attached with DOM.

Solution I tried is as follows:

 protected void clickOnElement(By by) {
        try {
            waitForElementToBeClickableBy(by).click();
        } catch (StaleElementReferenceException e) {
            for (int attempts = 1; attempts < 100; attempts++) {
                try {
                    waitFor(500);
                    logger.info("Stale element found retrying:" + attempts);
                    waitForElementToBeClickableBy(by).click();
                    break;
                } catch (StaleElementReferenceException e1) {
                    logger.info("Stale element found retrying:" + attempts);
protected WebElement waitForElementToBeClickableBy(By by) {
        WebDriverWait wait = new WebDriverWait(getDriver(), 10);
        return wait.until(ExpectedConditions.elementToBeClickable(by));

In above code I first try to wait and then click on element if exception occurs then I catch it and try to loop it as there is a possibility that still all elements may not be loaded and again exception can occur.

Your definition of a stale element is not correct. You should probably take a few minutes and google the definition. – JeffC Oct 10, 2021 at 21:00 @cruisepandey I guess this is the same implementation but in C# rather than Java - although almost identical? – kenny_k Jun 3, 2021 at 7:43

The problem is by the time you pass the element from Javascript to Java back to Javascript it can have left the DOM.
Try doing the whole thing in Javascript:

driver.executeScript("document.querySelector('#my_id')?.click()") 
                This isn't going to do really anything different than using the Java method except you don't get any Intellisense with the JS code, you bring in a whole new category of problems by using JS inside Java, etc.
– JeffC
                Oct 10, 2021 at 21:06
                It's actually completely different. It avoids stale element issues by getting that dom element immediately. If you miss intellisense you should switch to Javascript which makes more sense anyway.
– pguardiario
                Oct 10, 2021 at 23:09
                No, it actually doesn't get it any faster. If nothing else, it's probably slightly slower because the JS has to be interpreted instead of just using driver.findElement(By.id()).click();. The end result is exactly the same.
– JeffC
                Oct 10, 2021 at 23:13
                This question is about Java. You are suggesting that loading Java libraries, calling a Java method that in turn interprets JS code, evaluates it, and then comes all the way back out is faster than just calling the Java method? I don't think so.
– JeffC
                Oct 10, 2021 at 23:34

Maybe it was added more recently, but other answers fail to mention Selenium's implicit wait feature, which does all the above for you, and is built into Selenium.

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

This will retry findElement() calls until the element has been found, or for 10 seconds.

Source - http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp

Just to eliminate any confusion on versions, even in the latest version of Selenium implicitlyWait() does NOT prevent StaleElementReferenceException. I use a method which calls in a loop with sleep, till success or fixed count. – Angsuman Chakraborty Feb 8, 2017 at 7:48 Selenium webdriver: stale element reference: element is not attached to the page document See more linked questions