New-tab handoffs can leave a Selenium test sending commands to the wrong browser context after an OAuth login, payment redirect, document preview, or popup creates a second tab or window. WebDriver does not follow the desktop browser focus automatically; the script must switch to the handle for the context it needs to control.
WebDriver represents every tab and window as a window handle. In Python, driver.current_window_handle returns the active handle, driver.window_handles lists the open handles, and driver.switch_to.window(handle) moves later commands to that handle. Selenium 4 can also create a tab or window with driver.switch_to.new_window(“tab”) or driver.switch_to.new_window(“window”).
Store the original handle before opening the second context, wait for the expected handle count when the application opens a tab, and return to a valid handle before continuing. Closing a tab without switching back leaves later commands aimed at a closed page and can raise NoSuchWindowException.
from urllib.parse import quote from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait def data_page(title, heading): html = f"""<!doctype html> <html lang="en"> <head><meta charset="utf-8"><title>{title}</title></head> <body><h1>{heading}</h1></body> </html>""" return "data:text/html;charset=utf-8," + quote(html) options = Options() options.add_argument("--headless=new") options.add_argument("--window-size=1280,720") driver = webdriver.Chrome(options=options) try: wait = WebDriverWait(driver, 10) driver.get(data_page("Original Context", "Original window")) original_handle = driver.current_window_handle print(f"original_title: {driver.title}") print(f"handles_before: {len(driver.window_handles)}") driver.switch_to.new_window("tab") new_handle = driver.current_window_handle driver.get(data_page("Second Context", "Second tab")) wait.until(EC.title_is("Second Context")) print(f"handles_after_open: {len(driver.window_handles)}") print(f"active_after_new_tab: {driver.title}") driver.switch_to.window(original_handle) print(f"active_after_return: {driver.title}") driver.switch_to.window(new_handle) driver.close() driver.switch_to.window(original_handle) print(f"handles_after_close: {len(driver.window_handles)}") print(f"active_final: {driver.title}") finally: driver.quit()
If Chrome does not launch, install Selenium for Python or configure the Chrome driver first. Use window instead of tab in new_window() when the browser context must open as a separate window.
Related: How to install Selenium WebDriver for Python
Related: How to configure ChromeDriver for Selenium
$ python3 switch_window_tab.py original_title: Original Context handles_before: 1 handles_after_open: 2 active_after_new_tab: Second Context active_after_return: Original Context handles_after_close: 1 active_final: Original Context
handles_after_open should be 2 while the second tab is open, and active_after_return should show the original title after switch_to.window(original_handle).
from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC original_handle = driver.current_window_handle driver.find_element(By.LINK_TEXT, "Open report").click() wait.until(EC.number_of_windows_to_be(2)) new_handle = (set(driver.window_handles) - {original_handle}).pop() driver.switch_to.window(new_handle)
Store the original handle before the click. Do not rely on a fixed index when another popup, extension page, or authentication window can appear first.
$ rm switch_window_tab.py