How to avoid a StaleElementReferenceException in Selenium?

I use a lot of Selenium tests using Java. Sometimes my tests fail due to a StaleElementReferenceException . Could you suggest some approaches to increase the stability of tests?

+64
java selenium-webdriver
Oct 19 '12 at 4:45
source share
13 answers

This can happen if the DOM operation that occurs on the page temporarily makes the item inaccessible. To address these cases, you can try to access an element several times in a loop before finally throwing an exception.

Try this great 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; } 
+66
Oct 19
source share

I had this problem with interruptions. Unbeknownst to me, BackboneJS ran on the page and replaced the element I was trying to click. My code was as follows.

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

Which, of course, functionally coincides with this.

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

Sometimes it happens that javascript would replace the checkoutLink element between search and click, i.e.

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

This rightfully threw a StaleElementReferenceException when trying to click a link. I could not find a reliable way to tell WebDriver to wait for javascript to finish, so this is how I ended up solving 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 constantly try to click the link, ignoring the values ​​of StaleElementReferenceExceptions until a click is reached or a timeout is reached. I like this solution because it eliminates the need to write repeat logic and uses only the built-in WebDriver constructs.

+59
Aug 24 '14 at 9:34
source share

This is usually related to updating the DOM, and you are trying to access the updated / new item, but the DOM has been updated, so you have the wrong link.

Work around this by first using the explicit wait for the item to make sure the update is complete, then get a new link to the item again.

Here is some psuedo code for illustration (adapted from some C # code that I use for EXACTLY ):

 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!

+13
Oct 19 '12 at 20:29
source share

Kenny's solution is good, but it can be written more elegantly

 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 in any case, the best solution is to rely on the Selenide library, which handles such things and much more. (instead of links to elements, it processes proxies, so you never have to deal with obsolete elements, which can be quite difficult). selenide

+11
Jan 06 '17 at 9:20
source share

The cause of the StaleElementReferenceException has already been outlined: DOM updates between finding and executing something with an element.

For the click problem, I recently used a solution similar to 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 β€œlinking” of your own Selenium ExpectedConditions through ExpectedConditions.refreshed() . It actually waits and checks to see if the corresponding item has been updated within the specified timeout, and further expects the item to become active.

See the documentation for the updated method .

+7
Oct 27 '16 at 14:33
source share

In my project, I introduced the concept of StableWebElement. This is a wrapper for WebElement that is able to determine if a Stale element is and find a new link to the original element. I added helper methods to find items that return a StableWebElement instead of a WebElement, and the problem with the StaleElementReference has disappeared.

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

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

+3
Oct 01 '17 at 8:47 on
source share

A solution in C # would be:

Helper Class:

 internal class DriverHelper { private IWebDriver Driver { get; set; } 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) { try { //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; } } } 

Vocation:

  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.

+1
Feb 16 '16 at 10:48
source share

Kenny's solution is deprecated, use this, I use a double click action class, but you can do anything you want.

 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; } }); 
+1
Dec 15 '18 at 16:11
source share

This works for me (works 100%) using C #

 public Boolean RetryingFindClick(IWebElement webElement) { Boolean result = false; int attempts = 0; while (attempts < 2) { try { webElement.Click(); result = true; break; } catch (StaleElementReferenceException e) { Logging.Text(e.Message); } attempts++; } return result; } 
0
Feb 19 '18 at 10:48
source share

The problem is that when you pass an element from Javascript to Java back to Javascript, it can leave the DOM.
Try to do all this in Javascript:

 driver.executeScript("document.querySelector('#my_id').click()") 
0
Jan 03 '19 at 1:27
source share

try it

 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 } } 
0
Feb 14 '19 at 13:45
source share

I found a solution here . In my case, the item becomes unavailable if you leave the current window, tab or page and return again.

.ignoring (StaleElement ...) ,. refreshed (...) and elementToBeClicable (...) did not help, and I got an exception for the string act.doubleClick(element).build().perform(); .

Using a 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)))); } 
0
Aug 22 '19 at 13:01
source share

It may have been added recently, but other answers do not mention the Selenium implicit wait function, which does all of the above for you and is built into Selenium.

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

This will cause findElement() to be findElement() again until the item is found, or within 10 seconds.

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

-four
Oct 14 '15 at 13:37
source share



All Articles