1

Okay! So I am using the selenium chrome driver(32 bit) on windows 8.

I have set the implicit wait as below:

DesiredCapabilities des=DesiredCapabilities.chrome();
ChromeOptions options = new ChromeOptions();
options.addArguments("window-size=1366,768");
des.setCapability(ChromeOptions.CAPABILITY, options);
dvr= new ChromeDriver(des);
    driver = new EventFiringWebDriver(dvr);
    driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

But at some point in my tests I am getting a staleElementException...as below:

I am not too worried about the staleElementException, what bothers me is the below line in the exception: Command duration or timeout: 14 milliseconds

When the timeout is already set to 30 Seconds implicitly then why am I getting the timeout of 14 milliseconds....Any suggestions or workaround will be appreaciate thanks!

public static WebElement grabElementByPureXPath(String xpath)
{
    WebElement element = null;
    int attempts=1;
    try
    {
        while(attempts<7)
        {
            try
            {
                element=driver.findElement(By.xpath(xpath));
            }
            catch(StaleElementReferenceException e){}
                attempts++;
        }
    }
    catch(Throwable t)
    {
        try
        {
            element=driver.findElement(By.cssSelector(xpath));
        }
        catch(Throwable T)
        {
            takeScreenShot(xpath);
            Assert.assertTrue(t.getMessage(),false);
        }
    }

    return element;
}

<-----------------------The Exception Below--------------------------------------------->

org.openqa.selenium.StaleElementReferenceException: stale element reference: element   is not attached to the page document
  (Session info: chrome=29.0.1547.76)
  (Driver info: chromedriver=2.1,platform=Windows NT 6.2 x86_64) (WARNING: The server did not provide any stacktrace information)
**Command duration or timeout: 14 milliseconds**
For documentation on this error, please visit: http://seleniumhq.org/exceptions/stale_element_reference.html
Build info: version: '2.35.0', revision: 'c916b9d', time: '2013-08-12 15:42:01'
System info: os.name: 'Windows 8', os.arch: 'amd64', os.version: '6.2', java.version: '1.7.0_25'
Session ID: aee4999cb9dc120f7e17629cc1621d7d
Driver info: org.openqa.selenium.chrome.ChromeDriver
Capabilities [{platform=WIN8, acceptSslCerts=true, javascriptEnabled=true, browserName=chrome, chrome={chromedriverVersion=2.1}, rotatable=false, locationContextEnabled=true, version=29.0.1547.76, cssSelectorsEnabled=true, databaseEnabled=true, handlesAlerts=true, browserConnectionEnabled=false, webStorageEnabled=true, nativeEvents=true, applicationCacheEnabled=false, takesScreenshot=true}]
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at org.openqa.selenium.remote.ErrorHandler.createThrowable(ErrorHandler.java:191)
    at org.openqa.selenium.remote.ErrorHandler.throwIfResponseFailed(ErrorHandler.java:145)
    at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:554)
    at org.openqa.selenium.remote.RemoteWebElement.execute(RemoteWebElement.java:268)
    at org.openqa.selenium.remote.RemoteWebElement.click(RemoteWebElement.java:79)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.openqa.selenium.support.events.EventFiringWebDriver$EventFiringWebElement$1.invoke(EventFiringWebDriver.java:327)
    at com.sun.proxy.$Proxy12.click(Unknown Source)
    at org.openqa.selenium.support.events.EventFiringWebDriver$EventFiringWebElement.click(EventFiringWebDriver.java:340)
    at testCases.TC5663.test5663(TC5663.java:87)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.rules.Verifier$1.evaluate(Verifier.java:35)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.junit.runners.Suite.runChild(Suite.java:127)
    at org.junit.runners.Suite.runChild(Suite.java:26)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

/**********---------------------**********************/


Anirudh
  • 2,286
  • 4
  • 38
  • 64

1 Answers1

6

EDIT - Your comment below this answer and my answer to it which is closer to what you originally asked for:

As per documented behavior the StaleElementException seems to be Implicit wait proof. This seems like a shortcoming of Selenium in my view.

It has nothing to do with implicit waits, really. When the element had been searched for, it was found! Then something changed, the element was unloaded and probably loaded again, but you can't know for sure where the one is. Consider this:

driver.findElements(By.className("hello"));

This finds 12 elements, from which you take the third one and try to act on it - but oops, it is stale now! What should Selenium do?

The most reasonable thing is probably to re-find all the elements and take the third one from them, again. But the page obviously changed somehow (because the element is stale) and in fact, our element is now not third, but fourth. And Selenium can't possibly know that. How to overcome this?


Original answer:

This is working as intended and it is documented behaviour.

Your grabElementByPureXPath() finds and returns the right element. It's a few moments later when you try to click() the found element when Selenium throws StaleElementReferenceException.

This often happens in situations like this:

  1. You click something that loads a new page asynchronously or at least changes it.
  2. You immediatelly (before the page load could finish) search for an element via grabElementByPureXPath() ... and you find it and store it!
  3. The page finally unloads and the new one loads up.
  4. You try to click() your previously found element, but now it's stale, even though the new page contains the same element, too. But from Selenium's view, that's just an identical twin of your original element, not the original element itself.

Implicit waits have nothing to do with this - the element was found but is now not existing anymore. Selenium probably could try to search again for the element using the original By object passed, but it intentionally doesn't.

You can try one of the solutions in my answer here: How to resolve, Stale element exception? if element is no longer attached to the DOM?


Final nitpicks on your method. I know you didn't ask for it, but I feel obliged to tell you that:

  1. You are finding the element 6 times every time. Even when it's found on the first attempt. Change your condition from

    while(attempts<7)
    

    to

    while ((element == null) && (attempts < 7))
    
  2. This:

    try
    {
        element=driver.findElement(By.xpath(xpath));
    }
    catch(StaleElementReferenceException e) {}
    

    never triggers. The StaleElementReferenceException cannot occur and never happens on driver.findElement(). It simply can't. The exception is never thrown from this method. It only throws NoSuchElementException.

  3. Try at least to comment your empty code blocks. It's mildly confusing to see code like this:

    catch(StaleElementReferenceException e){}
        attempts++;
    

    If I didn't look for such gotchas, I might assume that the attempts increment happens only on the StaleElementReferenceException or something like that. The usual and more readable form of the code above is:

    catch (StaleElementReferenceException e)
    {
        // do nothing
    }
    attempts++;
    

    Commenting your empty code blocks also eliminates the risk of somebody removing your code. If this weren't a try-catch block, but rather a no-arg constructor with no code, I'd highly suggest you to put a comment there to explain why the constructor must be explicitly stated there (and sometimes, it must). Otherwise, somebody would sooner or later delete it / modify it in the wrong way.

  4. Please don't simply retry 6 times when searching for elements. This is wrong for several reasons:

    • It brings a magic number to your code. Magic numbers are bad.
    • It's obviously arbitralily chosen and has no comment explaining why exactly is it 6.
    • Even if the method is documented to retry 6 times, saying "it will retry 6 times" says nothing to the user.
    • It will behave differently in different browsers or on computers under different load. Re-finding 6 times might take 10 ms on a fast computer and Chrome/Firefox when searching by id, but it could take 30 seconds when searching on IE7 by some complicated XPath expression or on a computer under heavy load, or on a remote machine.

    Consider using a timeout instead. It can be easily documented, behaves the same on every computer/browser and everyone understands what 500 ms means. The easiest way to write it is this:

    WebElement element = null;
    long targetTime = System.currentTimeMillis() + TIMEOUT_TIME;
    try
    {
        while ((element == null) && (System.currentTimeMillis() < targetTime))
        {
            try
            {
                element = driver.findElement(By.xpath(xpath));
            }
            catch (NoSuchElementException e) { /* do nothing */ }
        }
    }
    
  5. Don't catch Throwable t. If you really have to and you know why, leave a comment next to it. Catching a Throwable is considered a bad practice. See this or this. See the official Oracle Expections tutorial for more information.

  6. You shouldn't search for an element using By.cssSelector() in a method called grabElementByPureXPath(). A method should only do what its name (and documentation) suggests. Either remove/change that code, or change the name of the method to something like grabElementByXPathOrCssSelector().

  7. Instead of

    Assert.assertTrue(t.getMessage(), false);
    

    you can use

    Assert.fail(t.getMessage());
    

    It's shorter and it describes your intent more clearly.

Community
  • 1
  • 1
Petr Janeček
  • 37,768
  • 12
  • 121
  • 145
  • So even if an element is `stale`, I can have reference to it but if I try to do any action on it, it will throw `StaleElementReferenceException?` By the way, nice answer!!. – Code Enthusiastic Sep 29 '13 at 17:01
  • 1
    @CodeEnthusiastic Exactly that, yes. From the [`WebElement`](http://selenium.googlecode.com/svn/trunk/docs/api/java/org/openqa/selenium/WebElement.html) docs: _"All method calls will do a freshness check to ensure that the element reference is still valid. This essentially determines whether or not the element is still attached to the DOM. If this test fails, then an `StaleElementReferenceException` is thrown, and all future calls to this instance will fail."_ – Petr Janeček Sep 29 '13 at 17:04
  • I loved you enthusiasm Slanec....Big Thanks for reviewing my code and giving work around! Some of the points I noticed in your review are catchy though I did know most of them...this is my rough code...not finalized one!! Your suggestions are welcome again! I will try the workaround and get back!! – Anirudh Sep 30 '13 at 02:14
  • Also in reference to your comment about the staleElement...as per documented behavior the staleElementException seems to be implicit wait proof..this seems like a shortcoming of selenium in my view... – Anirudh Sep 30 '13 at 02:46
  • @Anirödh Well, it can't be really done in any other way. Consider this: `driver.findElements(By.className("hello"));`. This finds 12 elements, from which you take the third one and try to act on it - but oops, it is stale now! What should Selenium do? The most reasonable thing is probably to re-find all the elements and take the third one from them, again. But the page obviously changed somehow (because the element is stale) and in fact, our element is now not third, but fourth. And Selenium can't possibly know that. How to overcome this? – Petr Janeček Sep 30 '13 at 10:37
  • @Slanec Okay!! Well in case the element change in totality then obviously it has to throw staleElementException but what was happening in my case for single call of grabElementByPureXPath(String xpath) it was returning multiple times and skipping the catch block as well....we can't really say it's due to while loop..coz if it's the loop which is causing it to find the element multiple times then the control shouldn't reach the return statement...**The only way out seems to be Thread.Sleep(3000) which is an unstable kind of fix** – Anirudh Oct 01 '13 at 06:00