How to address "WebDriverException: Message: TypeError: can't access dead object" and wait for a frame to be available using Selenium and Python

Better approach is to make your own expected_condition. For example:

class nested_frames_to_be_available_and_switch:
    def __init__(self, *args):
        """
        :param args: locators tuple of nested frames (BY.ID, "ID1"), (BY.ID, "ID2"), ...
        """
        self.locators = args

    def __call__(self, driver):
        try:
            for locator in self.locators:
                driver.switch_to.frame(driver.find_element(*locator))
        except WebDriverException:
            driver.switch_to_default_content()
            return False
        return True

WebDriverWait(driver, 300).until(nested_frames_to_be_available_and_switch((By.ID, 'frame1'), (By.ID, 'frame1')))

But maybe there is no need for that.. To tell so I need to see your html DOM.


This error message...

WebDriverException: Message: TypeError: can't access dead object

...implies that there was an error while switching between <iframes>.

Some more information in terms of:

  • The relevant HTML
  • Presence of Frameset
  • Presence of Frames
  • Hierarchy of Nested Frames
  • Sequence of Frame Loading
  • Presence of JavaScript and AJAX Calls within the respective <iframe> tags

would have helped us to analyze the issue in a better way. However, at this point it is worth to mention that initially Selenium always gets the focus on the default_content. Here are a few approaches to work with nested <frames> and <framesets>:

  • If both frame1 and frame2 are at same level i.e. under the Top Level Browsing Context you need to:

    self.driver.switch_to.frame(self.driver.find_element_by_id("frame1"))
    self.driver.switch_to_default_content()
    self.driver.switch_to.frame(self.driver.find_element_by_id("frame2"))
    
  • If frame2 is nested within frame1, to switch from frame1 to frame2 you need to:

    self.driver.switch_to_default_content()
    self.driver.switch_to.frame(self.driver.find_element_by_id("frame1"))
    //code
    self.driver.switch_to.frame(self.driver.find_element_by_id("frame2"))
    
  • If frame2 and frame3 is within frame1 then, to switch from frame2 to frame3 you need to:

    self.driver.switch_to.frame(self.driver.find_element_by_id("frame2"))
    self.driver.switch_to.frame(self.driver.find_element_by_id("frame1"))
    self.driver.switch_to.frame(self.driver.find_element_by_id("frame3"))
    
  • If frame2 and frame3 is within a frameset23 which is within frame1 then, to switch from frame2 to frame3 you need to:

    self.driver.switch_to.frame(self.driver.find_element_by_id("frame2"))
    #ignore presence of frameset23
    self.driver.switch_to.frame(self.driver.find_element_by_id("frame1"))
    #ignore presence of frameset23
    self.driver.switch_to.frame(self.driver.find_element_by_id("frame3"))
    

Better approach with proper WebDriverWait

While dealing with iframe and frameset you need to induce WebDriverWait inconjunction with expected_conditions:

frame_to_be_available_and_switch_to_it()

As an example to switch from Top Level Browsing Context to an <iframe> an effective line of code will be:

  • Using frame ID:

    WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.ID,"frameID")))
    
  • Using frame NAME:

    WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.NAME,"frameNAME")))
    
  • Using frame CLASS_NAME:

    WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.frame_CLASS_NAME,"u_0_f")))
    
  • Using frame CSS_SELECTOR:

    WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR,"frame_CSS_SELECTOR")))
    
  • Using frame XPATH:

    WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH,"frame_XPATH")))
    

tl; dr

Ways to deal with #document under iframe