
How to Handle StaleElementReferenceException in Selenium

When performing Selenium automation testing, if you try to interact with a WebElement that’s no longer in the Document Object Model (DOM), Selenium will throw a StaleElementReferenceException.
This happens upon refreshing the page or updating the DOM. In such a scenario, the WebElement becomes stale.
To avoid StaleElementReferenceException in Selenium, always locate and interact with elements dynamically rather than storing their references. This means finding elements each time you need them instead of keeping them in variables.
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.
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 LambdaTest Selenium Playground website to demonstrate how the test throws a StaleElementReferenceException.
Test Scenario:
- Navigate to the LambdaTest Selenium Playground website
- Click the Table Data Search link
- Enter Status, “in progress” via “Filter by Task / Assignee / Status” field
- Go back and then again click the Table Data Search link.
- Enter Status, “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, the test script cannot enter a Status, although the field is visible after clicking the Table Data Search link.
Test Implementation
Let’s create a new test class TestExampleStaleElementReferenceException to implement the test scenario. The WebDriver instance would be declared at the class level as we would be requiring it in the setup(), tearDown() and test methods.
public class TestExampleStaleElementReferenceException {
private WebDriver driver;
//...
}
Next, we would be creating a setup() method that would setup the WebDriver sessio and run before any test runs.
@BeforeTest
public void setup () {
this.driver = new ChromeDriver ();
this.driver.manage ()
.window ()
.maximize ();
this.driver.manage ()
.timeouts ()
.implicitlyWait (Duration.ofSeconds (30));
this.driver.get ("https://www.lambdatest.com/selenium-playground/");
}
This setup() method will start a new instance of Chrome browser, maximize the browser window and set an implicit wait of 30 seconds. Next, it will navigate to the LambdaTest Selenium Playground website.
The tearDown() method will run after the test is executed and it will gracefully exit the WebDriver session.
@AfterTest
public void tearDown () {
this.driver.quit ();
}
The test createStaleElementReferenceException() method is created to demonstrate the StaleElementReferenceException in Selenium.
@Test
public void createStaleElementReferenceException () {
final WebElement pageLink = this.driver.findElement (By.linkText ("Table Data Search"));
pageLink.click ();
WebElement filterByField = this.driver.findElement (By.id ("task-table-filter"));
filterByField.sendKeys ("in progress");
this.driver.navigate ()
.back ();
pageLink.click ();
filterByField.sendKeys ("completed");
}

The navigation to the Selenium Playground website will be done in the setup() method. The createStaleElementReferenceException test 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:


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 and the test will run successfully.
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 () {
final WebElement pageLink = this.driver.findElement (By.linkText ("Table Data Search"));
pageLink.click ();
WebElement filterByField = this.driver.findElement (By.id ("task-table-filter"));
filterByField.sendKeys ("in progress");
this.driver.navigate ()
.back ();
pageLink.click ();
filterByField = this.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.

The below screenshot from IntelliJ IDE shows that the test ran successfully.

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.
public boolean retryUsingWhileLoopTryCatch (final By locator, final String value) {
boolean outcome = false;
int repeat = 0;
while (repeat <= 3) {
try {
this.driver.findElement (locator)
.sendKeys (value);
outcome = true;
break;
} catch (final StaleElementReferenceException exc) {
exc.printStackTrace ();
}
repeat++;
}
return outcome;
}
While Loop and Try-Catch Block
In the utility class, a method called retryUsingWhileLoopTryCatch() 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 class Utility {
//...
public boolean retryUsingWhileLoopTryCatch (final By locator, final String value) {
boolean outcome = false;
int repeat = 0;
while (repeat <= 3) {
try {
this.driver.findElement (locator)
.sendKeys (value);
outcome = true;
break;
} catch (final 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 testRetryUsingWhileLoopTryCatch () {
WebElement pageLink = this.driver.findElement (By.linkText ("Table Data Search"));
pageLink.click ();
final By filterByField = By.id ("task-table-filter");
this.utility.retryUsingWhileLoopTryCatch (filterByField, "in progress");
this.driver.navigate ()
.back ();
pageLink = this.driver.findElement (By.linkText ("Table Data Search"));
pageLink.click ();
this.utility.retryUsingWhileLoopTryCatch (filterByField, "completed");
}
The test calls the retryUsingWhileLoopTryCatch() 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.
The following screenshot of the test execution shows that the test was executed successfully without throwing the StaleElementReferenceException

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 boolean retryUsingForLoopTryCatch (final By locator, final String value) {
boolean outcome = false;
for (int repeat = 0; repeat <= 3; repeat++) {
try {
this.driver.findElement (locator)
.sendKeys (value);
outcome = true;
break;
} catch (final 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 retryUsingForLoopTryCatch() returns a boolean value, the last statement returns the actual value using the outcome variable. The following screenshot shows how the retryUsingForLoopTryCatch() method is used in the test.
@Test
public void testRetryUsingForLoopToHandleStaleException () {
WebElement pageLink = this.driver.findElement (By.linkText ("Table Data Search"));
pageLink.click ();
final By filterByField = By.id ("task-table-filter");
this.utility.retryUsingForLoopTryCatch (filterByField, "in progress");
this.driver.navigate ()
.back ();
pageLink = this.driver.findElement (By.linkText ("Table Data Search"));
pageLink.click ();
this.utility.retryUsingForLoopTryCatch (filterByField, "completed");
}
Like the while loop, the for loop calls a method from the helper class. The retryUsingForLoopTryCatch() 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.
The following screenshot of the test execution shows that the test ran successfully without StaleElementReferenceException

Using ExpectedConditions in Selenium
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 void chainMultipleExpectedConditions (final By locator, final String value) {
final WebDriverWait wait = new WebDriverWait (this.driver, Duration.ofSeconds (5));
wait.until (ExpectedConditions.refreshed (ExpectedConditions.presenceOfElementLocated (locator)));
this.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().

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 = this.driver.findElement (By.linkText ("Table Data Search"));
pageLink.click ();
final By filterByField = By.id ("task-table-filter");
this.utility.chainMultipleExpectedConditions (filterByField, "in progress");
this.driver.navigate ()
.back ();
pageLink = this.driver.findElement (By.linkText ("Table Data Search"));
pageLink.click ();
this.utility.chainMultipleExpectedConditions (filterByField, "completed");
}
The chainMultipleExpectedConditions() method is in the Utility class. With this solution, the method is called on two statements 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.
The following screenshot of the test execution shows that the test ran successfully finding the element without throwing StaleElementReferenceException

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 reinitializing the WebElement or implementing explicit waits to handle the StaleElementReferenceException
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.
Happy Testing!!