Error: Stale element is not attached to the page, after try statement

Question:

I have the following try statement, that basically finds a button that resets the current page I am in. In summary the page reloads,

try:
    reset_button = D.find_element(By.XPATH,"//button[starts-with(@class,'resetBtn rightActionBarBtn ng-star-inserted')]")
    reset_button.click()
    D.implicitly_wait(5)
    ok_reset_botton = D.find_element(By.ID,'okButton')
    D.implicitly_wait(5)
    print(ok_reset_botton)
    ok_reset_botton.click()
    D.implicitly_wait(5)
    # Trying to reset current worksheet
except:
    pass
print(D.current_url)
grupao_ab = D.find_element(By.XPATH,'//descendant::div[@class="slicer-restatement"][1]')
D.implicitly_wait(5)
grupao_ab.click()

The weird thing is every time that try statement get executed, I get the following log of error

selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: element is not attached to the page document

Which happens in the a following line of code according to the log

grupao_ab.click()

When I took a look at the reason given by selenium it say it is because the element is no longer on the given DOM, but the element grupao_ab, is not even being defined in that page so why it is giving me that error? If any extra information is needed just comment.

Asked By: INGl0R1AM0R1

||

Answers:

First of all, StaleElementReferenceException means that the web element reference you trying to access is no more valid. This normally happens after the page was reloaded. This is exactly what happens here.
What happened is as following: you clicked on reset button and immediately after that you collecting the grupao_ab element and shortly after that trying to click it. But between the moment you located the grupao_ab element with grupao_ab = D.find_element(By.XPATH,'//descendant::div[@class="slicer-restatement"][1]') and the line where you trying to click it, reloading started. So that previously collected web element, that actually is a reference to a physical element on the DOM, no more pointing to that web element.
What you can to do here is: after clicking on the refresh button set a short delay so that refreshing will start and after that wait for grupao_ab element to become clickable. WebDriverWait expected_conditions explicit waits should be used for that.
Also, you should understand that D.implicitly_wait(5) is not a pause command. It sets the timeout for find_element and find_elements methods to wait for presence of the searching element. Normally we never set this timeout at all since it’s better to use WebDriverWait expected_conditions explicit waits, not implicitly_wait implicitly waits. And you should never mix these two types of waits.
And even if you want to set implicitly_wait to some value normally no need to set it again, this setting is applied to the entire driver session.
Please try changing your code as following:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 20)

try:
    wait.until(EC.element_to_be_clickable((By.XPATH, "//button[starts-with(@class,'resetBtn rightActionBarBtn ng-star-inserted')]"))).click()
    wait.until(EC.element_to_be_clickable((By.ID, "okButton"))).click()
    print(ok_reset_botton)
    time.sleep(0.5) # a short pause to make reloading started
except:
    pass
print(D.current_url)
#wait for the element on refreshed page to become clickable
wait.until(EC.element_to_be_clickable((By.XPATH, '//descendant::div[@class="slicer-restatement"][1]'))).click()
Answered By: Prophet