Hero Background

Power Your Software Testing with AI Agents and Cloud

The Native AI-Agentic Cloud Platform to Supercharge Quality Engineering. Test Intelligently and Ship Faster.

Selenium JavaAutomationTutorial

How to Handle StaleElementReferenceException in Selenium

Learn how to handle StaleElementReferenceException in Selenium with effective strategies to ensure stable and reliable test automation.

Last Updated on:

A StaleElementReferenceException in Selenium is thrown when your script acts on a WebElement that is no longer attached to the page's DOM, and you handle it by locating the element again. Selenium Java raises it as org.openqa.selenium.StaleElementReferenceException after a page reload, a browser back navigation, or a JavaScript update that replaces the node the reference pointed to.

This guide explains what the exception is, when it occurs, an example that reproduces it, three ways to handle it, and how to prevent it.

Key Takeaways

  • Selenium throws a StaleElementReferenceException when a script acts on a WebElement that has been removed from the DOM or is no longer attached to the current page document.
  • A page reload, a browser back navigation, or a JavaScript re-render can each turn a previously found WebElement reference stale.
  • Locating the WebElement again with findElement() right before the action is the most direct fix for a StaleElementReferenceException.
  • A while loop or a for loop around a try-catch block can retry a Selenium action a fixed number of times when an element is briefly detached.
  • WebDriverWait with ExpectedConditions.refreshed() and presenceOfElementLocated() waits for the element to be attached again before Selenium interacts with the element.
  • Storing By locators instead of WebElement objects stops Selenium Java tests from reusing a reference that belongs to an old DOM.

What Is a StaleElementReferenceException?

The StaleElementReferenceException is a runtime error arising when an object’s state changes while your code is in the midst of using it.

This shift can result from a page refresh or user-triggered events altering your document’s structure. The reference to the modified object remains valid but no longer corresponds to your intended target. Attempting to use it will result in a StaleElementReferenceException.

Youtube thumbnail

When Does StaleElementReferenceException Occur?

Selenium throws a StaleElementReferenceException when the reference to the element we interact with is no longer attached to the actual DOM of the web page.

It throws a StaleElementReferenceException in one of the following two circumstances.

  • The element has been removed from the DOM.
  • The element is not attached to the page document.

When you perform automation testing, there is an edge case where Selenium WebDriver will return a StaleElementReferenceException if the element changes its type. Edge cases involve extreme situations at the minimum or maximum range of a possible condition.

Example of StaleElementReferenceException in Selenium

Let’s take an example of the TestMu AI Selenium Playground to demonstrate how the test throws a StaleElementReferenceException.

Test Scenario:

  • Navigate to the TestMu AI Selenium Playground website.
  • Click the Table Data Search link.
  • Enter Status, “i.e., in progress” via Filter by Task / Assignee / Status field.
  • Go back and then again click the Table Data Search link.
  • Enter Status, “i.e., completed” via Filter by Task / Assignee / Status field.

The root of this StaleElementReferenceException is navigating to the previous page. Clicking the back button causes the Filter by Task / Assignee / Status field to be detached from the DOM. Therefore, our test script cannot enter a Status, although the field is visible after clicking the Table Data Search link.

Test Implementation:

Starting with the BaseTest class, the Selenium code sets up and tears down every test. The online Selenium Grid by TestMu AI has been used for demonstration.

TestMu AI is an AI-powered cloud testing platform that offers Selenium automation at scale. You can exponentially increase your browser coverage by running your Selenium scripts on a test automation cloud of 3000+ desktop and mobile environments, ensuring a seamless user experience across all browsers and OSes.

Test across 3000+ browser and OS environments with TestMu AI

Code Walkthrough:

A new BaseTest class is created that will configure the RemoteWebDriver class to run the tests on the TestMu AI cloud grid.

public class BaseTest {

   RemoteWebDriver driver;
   //…
}

The following is the setup() method created inside the BaseTest class. It will create an instance of the RemoteWebDriver class, apply the implicit wait, and load the base URL to navigate to the Selenium Playground website.

For more information on RemoteWebDriver, refer to this blog on Selenium RemoteWebDriver.

@BeforeTest
public void setup() {

   String USERNAME = System.getenv("LT_USERNAME") == null ? "LT_USERNAME" : System.getenv("LT_USERNAME");
   String ACCESS_KEY = System.getenv("LT_ACCESS_KEY") == null ? "LT_ACCESS_KEY" : System.getenv("LT_ACCESS_KEY");
   String GRID_URL = "@hub.lambdatest.com/wd/hub";

   try {
       this.driver = new RemoteWebDriver(new URL("http://" + USERNAME + ":" + ACCESS_KEY + GRID_URL), getChromeOptions());
   } catch (final MalformedURLException e) {
       System.out.println("Could not start the remote session on LambdaTest cloud grid");
   }

   this.driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(20));
   this.driver.get("https://www.testmuai.com/selenium-playground/");
}

The LT_USERNAME, LT_ACCESS_KEY, and Grid_URL are required to run the web automation tests on the TestMu AI cloud grid. These values will be read on run time using the environment variables LT_USERNAME and LT_ACCESS_KEY.

While creating a new instance of the RemoteWebDriver class, the capabilities must also include details for the browser, browser version, platform, build name, test name, etc. These capabilities will be provided using the getChromeOptions() method. You can generate your custom desired capabilities using the Automation Capabilities Generator.

public ChromeOptions getChromeOptions() {
   final var browserOptions = new ChromeOptions();
   browserOptions.setPlatformName("Windows 10");
   browserOptions.setBrowserVersion("122.0");
   final HashMap<String, Object> ltOptions = new HashMap<String, Object>();
   ltOptions.put("project", "Stale Element Reference Exception Blog");
   ltOptions.put("build", "Selenium Playground - Table Data search");
   ltOptions.put("name", "Demo Stale Element Reference Exception");
   ltOptions.put("w3c", true);
   ltOptions.put("plugin", "java-testNG");

   browserOptions.setCapability("LT:Options", ltOptions);

   return browserOptions;

}

The getChromeOptions() method returns the object of the ChromeOptions class. Now, a new test class, TestExampleStaleElementReferenceException, extends the BaseTest class.

public class TestExampleStaleElementReferenceException extends BaseTest{

//..

}

The test createStaleElementReferenceException() method is created to demonstrate the StaleElementReferenceException in Selenium.

@Test
public void createStaleElementReferenceException() {
   WebElement pageLink = driver.findElement(By.linkText("Table Data Search"));
   pageLink.click();
   WebElement filterByField = driver.findElement(By.id("task-table-filter"));

   filterByField.sendKeys("in progress");
   driver.navigate().back();
   pageLink.click();
   filterByField.sendKeys("completed");
}
Github

The navigation to the Selenium Playground website will be done using the BaseTest class. This method will click on the Table Data Search link on the homepage of the Selenium Playground website and will open the respective web page.

Next, it will locate the Filter by Task / Assignee / Status field and type in the word in progress, eventually fetching all the records with the respective status.

The test script will then perform browser back navigation to load the previous page, i.e., the home page of the website. On the home page, it will again click the Table Data Search link. While executing the next script to enter the value completed in the Filter by Task / Assignee / Status field it will throw the StaleElementReferenceException.

The screenshots below provide more information regarding the StaleElementReferenceException:

StaleElementReferenceExceptionStaleElementReferenceException

On a side note, you can further enhance your Selenium automation testing by leveraging AI testing agents like KaneAI.

KaneAI is a GenAI native smart AI test assistant for high-speed quality engineering teams. With its unique AI-driven features for test authoring, management, and debugging, KaneAI allows teams to create and evolve complex test cases using natural language.

Automate web and mobile tests with KaneAI by TestMu AI

How to Handle StaleElementReferenceException?

The StaleElementReferenceException can be handled in multiple ways depending on why the exception was thrown. Let’s see how to fix the StaleElementReferenceException by:

  • Re-Initializing the WebElement
  • Using Loops and Try-Catch Blocks
  • Using ExpectedConditions

Re-initializing the WebElement

In the example code above, if we re-initialize the filterByField element before entering the completed status, then the StaleElementReferenceException will not be thrown.

There was no reference to filterByField because it was not attached to the page document once the page was reloaded. The code below displays how to re-initialize filterByField to avoid the exception.

@Test


    public void createStaleElementReferenceException() {


        WebElement pageLink = driver.findElement(By.linkText("Table Data Search"));


        pageLink.click();


        WebElement filterByField = driver.findElement(By.id("task-table-filter"));


        filterByField.sendKeys("in progress");


        driver.navigate().back();


        pageLink.click();


        filterByField = driver.findElement(By.id("task-table-filter"));


        filterByField.sendKeys("completed");

Notice the second last line in the above code; it finds the element before entering the completed status. As a result, the StaleElementReferenceException is not thrown, and the test script successfully searches for a completed status and returns the correct results.

The screenshot below shows how the completed status is searched and returned on the Table Search filter page.

StaleElementReferenceException

Using Loops and Try-Catch Block

In some instances, the element is temporarily not reachable through the DOM. Hence, we can access the element multiple times in a loop until it becomes available.

Within our desired loop, we implement a try-catch block and attempt to make a connection with the element. For this, we will use while and for loops.

While Loop and Try-Catch Block

In the helper class, a method called retryUsingWhileLoop_TryCatch() is created to handle the StaleElementReferenceException. It receives a By locator and String value as the parameters. The boolean outcome variable is initialized to false, and the int repeat variable is set to 0 initially.

public static boolean retryUsingWhileLoop_TryCatch(By locator, String value) {


   boolean outcome = false;


   int repeat = 0;


   while (repeat <= 3) {


       try {


           driver.findElement(locator).sendKeys(value);


           outcome = true;


           break;


       } catch (StaleElementReferenceException exc) {


           exc.printStackTrace();


       }


       repeat++;


   }


   return outcome;


}

The while loop starts with a condition to repeat less than or equal to 3. Next is the try block, which tries to find the element and enter a status value. If the outcome is false, the code continues up to 3 times.

Usually, the second iteration finds the element and acts on the element. If the element is found, the outcome is true. The following code shows how a locator and value are passed.

@Test


    public void testRetryUsingWhileLoop_TryCatch() {


        WebElement pageLink = driver.findElement(By.linkText("Table Data Search"));


        pageLink.click();


        By filterByField = By.id("task-table-filter");


        retryUsingWhileLoop_TryCatch(filterByField, "in progress");


        driver.navigate().back();


        pageLink = driver.findElement(By.linkText("Table Data Search"));


        pageLink.click();


        retryUsingWhileLoop_TryCatch(filterByField, "completed");


    }

The test calls the retryUsingWhileLoop_TryCatch() method and sends two arguments: filterByField and a value. The method is called twice in the tests with the same filterByField locator but different values (in progress and completed).

Both calls were a success and did not return a StaleElementReferenceException.

  • The in progress status returned 1 task.
  • The completed status returned 3 tasks.

For Loop and Try-Catch Block

We can handle the StaleElementReferenceException using a for loop similar to a while loop. Each loop has a try-catch block and initializes the boolean variable, i.e., outcome to false.

The following is a code snippet displaying a try-catch block within a for loop:

public static boolean retryUsingForLoop_TryCatch(By locator, String value) {


   boolean outcome = false;


   for(int repeat=0; repeat<=3; repeat++) {


       try {


           driver.findElement(locator).sendKeys(value);


           outcome = true;


           break;


       } catch(StaleElementReferenceException exc) {


           exc.printStackTrace();


       }


   }


   return outcome;


}

The method begins with the for loop by initializing the repeat variable to 0, followed by the condition to cycle less than or equal to 3. Next, the repeat variable increments by 1 (repeat ++). Like the while loop, this for loop tries to find the element and enter a value.

If the element is found, the outcome is true, and the loop is terminated immediately with the break statement. The purpose of the catch block is to handle the StaleElementReferenceException.

Since the method retryUsingForLoop_TryCatch() returns a boolean value, the last statement returns the actual value using the outcome variable. The following screenshot shows how the retryUsingForLoop_TryCatch() method is used in the test.

@Test


    public void testRetryUsingForLoopToHandleStaleException() {


        WebElement pageLink = driver.findElement(By.linkText("Table Data Search"));


        pageLink.click();


        By filterByField = By.id("task-table-filter");


        retryUsingForLoop_TryCatch(filterByField, "in progress");


        driver.navigate().back();


        pageLink = driver.findElement(By.linkText("Table Data Search"));


        pageLink.click();


        retryUsingForLoop_TryCatch(filterByField, "completed");


    }

Like the while loop, the for loop calls a method from the helper class. The retryUsingForLoop_TryCatch() method is called first for setting the in progress status in the filterByField, and next, it is again called to set the completed status.

  • The in progress status returns 1 task.
  • The completed status returns 3 tasks.

Using ExpectedConditions

Sometimes, JavaScript automatically updates the web page between finding an element and performing an action on the element. Respectively, a timing issue can occur and generate a StaleElementReferenceException.

Thanks to Selenium, it can manage the StaleElementReferenceException by implementing methods from the ExpectedConditions class.

The following is an example code by chaining multiple methods using the ExpectedConditions in Selenium:

public static void chainMultipleExpectedConditions(By locator, String value) {


   WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));


   wait.until(ExpectedConditions.refreshed(


           ExpectedConditions.presenceOfElementLocated(locator)));


   driver.findElement(locator).sendKeys(value);


}

This code creates an instance of WebDriverWait with the wait as the object reference variable. The parameters are the driver (instance of WebDriver) and Duration.ofSeconds(5):

  • The driver controls the Chrome browser.
  • Duration.ofSeconds(5) will wait to find the element for up to 5 seconds.

WebDriverWait is a class that helps to develop an explicit dynamic wait statement. If the element is not found, then a TimeoutException shows up. The key to handling a StaleElementReferenceException is using the wait.until() method with the ExpectedConditions class.

The wait.until() provides access to the ExpectedConditions class, which has many methods. The following screenshot shows some methods, including refreshed() and presenceOfElementLocated().

presenceOfElementLocated()

Handling StaleElementReferenceException uses multiple methods. It dynamically waits and checks if the element is refreshed for up to 5 seconds, then waits an additional five seconds for the element to be present.

In some cases, only one method, such as stalenessOf() from the ExpectedConditions class, is sufficient for overseeing a StaleElementReferenceException.

The following screenshot displays how the @Test annotation calls the chainMultipleExpectedCondtions() method.

@Test


    public void testChainExpectedConditionsToHandleStaleException() {


        WebElement pageLink = driver.findElement(By.linkText("Table Data Search"));


        pageLink.click();


        By filterByField = By.id("task-table-filter");


        chainMultipleExpectedConditions(filterByField, "in progress");


        driver.navigate().back();


        pageLink = driver.findElement(By.linkText("Table Data Search"));


        pageLink.click();


        chainMultipleExpectedConditions(filterByField, "completed");


    }


}

The chainMultipleExpectedConditions() method is in the helper class. With this solution, the method is called on two lines in the test. It is comparable to the other methods by sending two arguments: filterByField and Status.

  • The first sends the value in progress in the filterByField, with 1 Task showing up in the results.
  • When called the second time, it sends the completed value in the filterByField, with 3 tasks showing up in the results.
Note

Note: Run your Selenium scripts across 3000+ browser environments. Try TestMu AI Now!

All the tests were executed successfully on the TestMu AI cloud grid. The build details window provides us with the granular details of the test execution. These details include screenshots, video logs, etc, that can be used further for analysis in case of test failures.

LambdaTest dashboards

Moreover, you can also watch the below tutorial to learn how to handle StaleElementReferenceException in Selenium:

Youtube thumbnail

Subscribe to the TestMu AI YouTube Channel and stay updated with the latest tutorials. In addition, you can dive deep into handling other Selenium exceptions.

How Do You Prevent StaleElementReferenceException in Selenium?

You prevent a StaleElementReferenceException by locating each element right before you use it and by waiting for DOM updates to finish. Handling the exception recovers a failed step, while prevention stops the stale reference from being created.

The following practices keep element references current in Selenium Java tests:

  • Store locators, not elements: Keep a By locator in a field and call findElement() at the moment of each action. A WebElement saved earlier can point to a node that no longer exists.
  • Re-locate after navigation: Find elements again after navigate().back(), navigate().refresh(), or any click that loads a new page. Each page load builds a new document, so references from the old document are stale.
  • Wait for the old element to detach: Pass the old reference to ExpectedConditions.stalenessOf() after the action that triggers the update. The wait returns once the old node leaves the DOM, so the next findElement() call returns the new node.
  • Retry inside the wait: Call ignoring(StaleElementReferenceException.class) on a WebDriverWait and locate the element inside the until() function. The wait then repeats the lookup until it succeeds or the timeout expires.
  • Avoid @CacheLookup on dynamic elements: A PageFactory field annotated with @FindBy finds the element again on every call. Adding @CacheLookup keeps the first result, which goes stale when the page re-renders that element.

Single-page apps built with React, Angular, or Vue can replace a DOM node without a page load. Apply these practices to table rows, list items, and form fields that update in place, not only to elements on pages that reload.

Conclusion

StaleElementReferenceException occurs when the WebElement has been removed from the DOM, or the element is not attached to the page. There are multiple ways, such as using explicit waits or reinitializing the WebElement.

However, in my experience, this exception can be avoided by implementing the correct test strategy for automation testing, such as checking if the WebElement is present or visible on the page before interacting with it. Another best practice is to use the stable locators to locate the WebElement.

Author

...

Faisal Khatri

Blogs: 40

  • Twitter
  • Linkedin

Mohammad Faisal Khatri is a Software Testing Professional with 17+ years of experience in manual exploratory and automation testing. He currently works as a Senior Testing Specialist at Kafaat Business Solutions and has previously worked with Thoughtworks, HCL Technologies, and CrossAsyst Infotech. He is skilled in tools like Selenium WebDriver, Rest Assured, SuperTest, Playwright, WebDriverIO, Appium, Postman, Docker, Jenkins, GitHub Actions, TestNG, and MySQL. Faisal has led QA teams of 5+ members, managing delivery across onshore and offshore models. He holds a B.Com degree and is ISTQB Foundation Level certified. A passionate content creator, he has authored 100+ blogs on Medium, 40+ on TestMu AI, and built a community of 25K+ followers on LinkedIn. His GitHub repository “Awesome Learning” has earned 1K+ stars.

Reviewer

...

Navin Chandra

Reviewer

  • Linkedin

Navin Chandra is a Member of Technical Staff at TestMu AI (formerly LambdaTest), building the open-source automation that powers its Selenium and Appium cloud grid. A committer to both Selenium and Appium, he implemented WebDriver BiDi support in Selenium for real-time browser events and bidirectional control and is developing Apple's iOS RemoteXPC protocol in Appium to enable low-level wireless communication with iOS system services. He contributes to Selenium across multiple language bindings as a member of the Selenium GitHub organization. He has served as a Google Summer of Code mentee and mentor at openSUSE and an LFX mentee at CNCF's KubeArmor, and is a SUSE Certified Deployment Specialist. Navin holds a B.Tech in Computer Science.

Add to Google preferred sources

Summarise with AI

Copied to Clipboard!
...

3000+ Browsers. One Platform.

See exactly how your site performs everywhere.

Try it free
...

Write Tests in Plain English with KaneAI

Create, debug, and evolve tests using natural language.

Try for free

Stale Element Exception in Selenium FAQs

Did you find this page helpful?

More Related Blogs

TestMu AI forEnterprise

Get access to solutions built on Enterprise
grade security, privacy, & compliance

  • Advanced access controls
  • Advanced data retention rules
  • Advanced Local Testing
  • Premium Support options
  • Early access to beta features
  • Private Slack Channel
  • Unlimited Manual Accessibility DevTools Tests