Home > OS >  Stale Element Reference Exception occurred even though I explicitly ignored the exception using igno
Stale Element Reference Exception occurred even though I explicitly ignored the exception using igno

Time:02-06

Why is the StaleElementReferenceException still raised even though I ignored the exception? The code will eventually run successfully after a few tries but how can I make it wait until the element is clickable to avoid any exception?

import sys
from selenium.common.exceptions import StaleElementReferenceException

    while True:
           try:
               WebDriverWait(driver, 10, ignored_exceptions=[StaleElementReferenceException]).until(ec.element_to_be_clickable((By.XPATH,"//button[contains(text(), 'Performance Summary')]"))).click()
               break              
           except Exception as e:
                  exc_type, exc_obj, exc_tb = sys.exc_info()                                
                  print(e, exc_type, exc_tb.tb_lineno)
                  print('Retrying...')

CodePudding user response:

From the source code of WebDriverWait:

class WebDriverWait:
    def __init__(
    self,
    driver,
    timeout: float,
    poll_frequency: float = POLL_FREQUENCY,
    ignored_exceptions: typing.Optional[WaitExcTypes] = None,
    ):
    """Constructor, takes a WebDriver instance and timeout in seconds.

    :Args:
     - driver - Instance of WebDriver (Ie, Firefox, Chrome or Remote)
     - timeout - Number of seconds before timing out
     - poll_frequency - sleep interval between calls
       By default, it is 0.5 second.
     - ignored_exceptions - iterable structure of exception classes ignored during calls.
       By default, it contains NoSuchElementException only.

    Example::

     from selenium.webdriver.support.wait import WebDriverWait \n
     element = WebDriverWait(driver, 10).until(lambda x: x.find_element(By.ID, "someId")) \n
     is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).\\ \n
             until_not(lambda x: x.find_element(By.ID, "someId").is_displayed())
    """
    self._driver = driver
    self._timeout = float(timeout)
    self._poll = poll_frequency
    # avoid the divide by zero
    if self._poll == 0:
        self._poll = POLL_FREQUENCY
    exceptions = list(IGNORED_EXCEPTIONS)
    if ignored_exceptions:
        try:
        exceptions.extend(iter(ignored_exceptions))
        except TypeError:  # ignored_exceptions is not iterable
        exceptions.append(ignored_exceptions)
    self._ignored_exceptions = tuple(exceptions)
    

It is worth to notice that ignored_exceptions is not iterable.

So NoSuchElementException being the default exception and StaleElementReferenceException being purposely added, can be ignored only once. Hence the second time StaleElementReferenceException is no more handled.

CodePudding user response:

The problem is that once you get a StaleElementReferenceException, you can't just wait it out. Here's how stale elements work.

element = driver.find_element(By.ID, "someId")
driver.refresh() # or anything that changes the portion of the page that 'element' is on
element.click() # throws StaleElementReferenceException

At this point a StaleElementReferenceException is thrown because you had a reference to the element and then lost it (the element reference pointer points to nothing). No amount of waiting is going to restore that reference.

The way to "fix" this is to grab the reference again after the page has changed,

element = driver.find_element(By.ID, "someId")
driver.refresh()
element = driver.find_element(By.ID, "someId") # refetch the reference after the page refreshes
element.click()

Now the .click() will work without error.

Most people that run into this issue are looping through a collection of elements and in the middle of the loop they click a link that navigates to a new page or something else that reloads or changes the page. They later return to the original page and click on the next link but they get a StaleElementReferenceException.

elements = driver.find_elements(locator)
for element in elements
    element.click() # navigates to new page
    # do other stuff and return to first page

The first loop works fine but in the second loop the element reference is dead because of the page change. You can change this loop to force the elements collection to be refetched at the start of each loop

for element in driver.find_elements(locator)
    element.click() # navigates to new page
    # do other stuff and return to first page

Now the loop will work. This is just an example but hopefully it will point you in the right direction to fix your code.

  • Related