SeleniumLibrary: StaleElementReferenceException with Wait* keywords

With Chrome 44.0.2403, Chrome driver 2.16 Selenium2Library 1.7.2 and selenium 2.46.1 when I use Wait* keywords, the StaleElementReferenceException might occur. Documentation says that keyword should only fail if condition is not met before the timeout, example for Wait Until Page Contains Element keyword, documentation says: Fails if timeout expires before the element appears.

But if StaleElementReferenceException happens, then I see this:

Selenium2Library.Wait Until Element Is Visible xpath=//span/span[text()="${menu_item}"], timeout=5s, error=Menu item ${menu_item} not found on page!
Documentation:  
Waits until element specified with `locator` is visible.
Start / End / Elapsed:  20150730 14:24:49.623 / 20150730 14:24:50.240 / 00:00:00.617
00:00:00.376KEYWORD: common_library_imports.Selenium2library Common Failure
14:24:49.624    TRACE   Arguments: [ u'xpath=//span/span[text()="XXXXXX"]' | timeout=u'5s' | error=u'Menu item XXXXXX not found on page!' ]   
14:24:49.624    DEBUG   POST http://127.0.0.1:55614/session/7f76eac90d05725647286d5bdf089b4a/elements {"using": "xpath", "sessionId": "7f76eac90d05725647286d5bdf089b4a", "value": "//span/span[text()=\"XXXXXX\"]"}   
14:24:49.829    DEBUG   Finished Request    
14:24:49.831    DEBUG   GET http://127.0.0.1:55614/session/7f76eac90d05725647286d5bdf089b4a/element/0.4323001531884074-3/displayed {"sessionId": "7f76eac90d05725647286d5bdf089b4a", "id": "0.4323001531884074-3"}  
14:24:49.855    DEBUG   Finished Request    
14:24:50.239    FAIL    StaleElementReferenceException: Message: stale element reference: element is not attached to the page document
  (Session info: chrome=44.0.2403.125)
  (Driver info: chromedriver=2.14.313457 (3d645c400edf2e2c500566c9aa096063e707c9cf),platform=Windows NT 6.1 SP1 x86_64)
14:24:50.239    DEBUG   Traceback (most recent call last):
  File "<string>", line 2, in wait_until_element_is_visible
  File "C:\Python27\lib\site-packages\Selenium2Library\keywords\keywordgroup.py", line 15, in _run_on_failure_decorator
    return method(*args, **kwargs)
  File "C:\Python27\lib\site-packages\Selenium2Library\keywords\_waiting.py", line 128, in wait_until_element_is_visible
    self._wait_until_no_error(timeout, check_visibility)
  File "C:\Python27\lib\site-packages\Selenium2Library\keywords\_waiting.py", line 237, in _wait_until_no_error
    timeout_error = wait_func(*args)
  File "C:\Python27\lib\site-packages\Selenium2Library\keywords\_waiting.py", line 121, in check_visibility
    visible = self._is_visible(locator)
  File "C:\Python27\lib\site-packages\Selenium2Library\keywords\_element.py", line 723, in _is_visible
    return element.is_displayed()
  File "C:\Python27\lib\site-packages\selenium\webdriver\remote\webelement.py", line 323, in is_displayed
    return self._execute(Command.IS_ELEMENT_DISPLAYED)['value']
  File "C:\Python27\lib\site-packages\selenium\webdriver\remote\webelement.py", line 402, in _execute
    return self._parent.execute(command, params)
  File "C:\Python27\lib\site-packages\Selenium2Library\webdrivermonkeypatches.py", line 11, in execute
    result = self._base_execute(driver_command, params)
  File "C:\Python27\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 175, in execute
    self.error_handler.check_response(response)
  File "C:\Python27\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 166, in check_response
    raise exception_class(message, screen, stacktrace)  
14:24:49.621    TRACE   Arguments: [ ${menu_item}=u'XXXXXX' | ${timeout}=u'10' ]

and keyword did fail immediately. But I did expect that keyword would simply re-try search for the element if the StaleElementReferenceException or any other selenium related error happens before the timeout.

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Comments: 50 (36 by maintainers)

Most upvoted comments

The reason I think Wait ... keywords could ignore the stale element errors is that they already ignore errors related to elements not existing in the first place. In practice these keywords execute conditions like self.find_element(locator).is_visible(). At the moment it’s fine if find_element() fails due to element not existing, but it’s a failure if element exists but is invalidated before calling is_visible().

In my opinion element not existing and element being invalid are so close to each others that they should be handled the same way. They both cause a failure with normal keywords, and I see no harm in both of them being ignored with wait keywords. If I’ve understood it correctly, now wait keyword passing or failing may depend on timing.

As I wrote above, I think these two error situations should be handled the same way. That could be easily and explicitly done like this:

    def _wait_until_worker(self, condition, timeout, default_error):
        max_time = time.time() + timeout
        error = None
        while time.time() < max_time:
            try:
                if condition():
                    return
            except (ElementNotFound, StaleElementReferenceException) as err:
                error = str(err)
            else:
                error = None
            time.sleep(0.2)
        raise AssertionError(error or default_error)

PS: That str(err) should actually be unicode(err) to avoid non-ASCII errors with Python 2.

User’s perspective: I’ve just spent a year in a shop dealing with apps written using a framework which was basically a stale element exception generator (SAPUI5, may you burn in hell). We used the SL monkeypatched to do exactly what Pekka posted above, and it was totally fine for us. We somehow knew the behavior is sort-of wrong but we were not able to fix the framework that caused the problems (different team, big corp). As you say, especially with Wait keywords it’s really a nuisance because when you are waiting on some transition, you kinda expect funky things to happen in the meantime in the DOM.

Holding my thumbs that a fix for this long-runner makes it into v3.3.0. That would be awesome. Thanks @aaltat and everybody else for your dedicated work with RF.

There is actually good reason why it happens randomly and why it happens “Wait Until Element Is Visible”. Understanding both of these, helps one to understand how to fix it and why fixing is actually so darn difficult.

In most of the selenium operations, like Clicks and "Wait Until Element Is Visible, there is actually two operations over the json wire protocol (from S2L to the browser). First one will locate the element and return reference to the element. Second operation will perform the click, based on the element reference. When the click is received, in the browser, selenium will check is the element reference still valid. If it’s not valid, error will be received.

The Webdriver wait functionality tries to remove the problem by allowing poll the element staleness. And polling will return when the element state is not anymore stale.

But thanks to modern browser technologies, element state is wery moving target. Example in a filter box, user types N number of letters and results is filtered. Now depending on the implementation of the application, element state can change from N * 2 (each key stroke causes state to change stale and back) to 0 (no filtering was required). Because it is impossible to know how many stale state change one should poll and how long one should wait, to be sure that error is not received it is hard to come up with universal solution to the problem.

But I am thinking and have prototypes for few solutions, but they all have their problems. And all of them require massive rewrite. But thanks from understanding and patience on the matter.