Fixing Selenium target window already closed error during fast alert handling

Root Cause Diagnosis

NoSuchWindowException: no such window: target window already closed from unknown error: web view not found means the driver sent a command to a browsing context that no longer exists. When an alert’s accept() or dismiss() triggers window.close() (or the popup hosting the alert closes itself), the driver’s current-window pointer still references the destroyed context. Any following command that needs a live context, such as find_element, title, current_url, or current_window_handle, fails. In fast flows, the same error appears when the window closes between the alert appearing and the next driver call.

Quick Fix

Switch to a surviving window handle immediately after the alert is dismissed. window_handles is safe to call when the current window is gone; current_window_handle is not.

alert = driver.switch_to.alert
alert.accept()
driver.switch_to.window(driver.window_handles[0])

Java equivalent:

driver.switchTo().alert().accept();
driver.switchTo().window(driver.getWindowHandles().iterator().next());

Step-by-Step Resolution

1. Capture window state before triggering the alert

main = driver.current_window_handle
before = set(driver.window_handles)

2. Wait for the alert explicitly

A fixed sleep or a bare switch_to.alert races the page script. Use an explicit wait:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

alert = WebDriverWait(driver, 10).until(EC.alert_is_present())

3. Read everything you need before dismissing

If the accept closes the window, alert properties are unreachable afterward.

text = alert.text
alert.accept()

4. Wait for the window count to settle, then switch

Do not assume the window closed or stayed open; wait for the state you expect.

from selenium.common.exceptions import TimeoutException

try:
    WebDriverWait(driver, 3).until(EC.number_of_windows_to_be(len(before) - 1))
except TimeoutException:
    pass  # window stayed open

remaining = driver.window_handles
driver.switch_to.window(main if main in remaining else remaining[0])

5. Wrap it in one reusable helper

Put this in utils/alerts.py (or your test framework’s helper module):

from selenium.common.exceptions import (
    NoSuchWindowException, NoAlertPresentException, TimeoutException,
)
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


def accept_alert_safely(driver, timeout=10):
    main = driver.current_window_handle
    count = len(driver.window_handles)

    alert = WebDriverWait(driver, timeout).until(EC.alert_is_present())
    text = alert.text
    alert.accept()

    try:
        WebDriverWait(driver, 2).until(EC.number_of_windows_to_be(count - 1))
    except TimeoutException:
        pass

    handles = driver.window_handles
    if not handles:
        raise RuntimeError("All windows closed after alert; session has no context")
    driver.switch_to.window(main if main in handles else handles[0])
    return text

6. Optionally let the browser auto-handle prompts

Where the alert content does not matter, set the W3C unhandledPromptBehavior capability so prompts do not interrupt commands. Options are accept, dismiss, accept and notify, dismiss and notify, and ignore. The W3C default is dismiss and notify.

from selenium.webdriver.chrome.options import Options

options = Options()
options.unhandled_prompt_behavior = "accept"
driver = webdriver.Chrome(options=options)
ChromeOptions options = new ChromeOptions();
options.setUnhandledPromptBehaviour(UnexpectedAlertBehaviour.ACCEPT);
WebDriver driver = new ChromeDriver(options);

7. Guard teardown

Calling driver.close() on an already-closed window raises the same exception. Use driver.quit() in fixtures, which ends the session regardless of window state.

@pytest.fixture
def driver():
    d = webdriver.Chrome()
    yield d
    d.quit()

Why This Fails for Some Users

  • Tab crash instead of intentional close: Chrome reports the same error when the renderer crashes. In Docker or CI, a small /dev/shm is the usual cause. Launch with --disable-dev-shm-usage or run the container with --shm-size=2g. Also add --no-sandbox only when running as root in a container.
  • beforeunload dialogs: A page with a beforeunload handler shows a prompt when the window closes. Chrome only shows it if the page received user interaction, so behavior differs between scripted and manual runs. Dismiss it with the alert API, or set unhandledPromptBehavior to accept.
  • Popup with window.opener logic: If the alert lives in a window.open() popup that closes itself, the main window handle is your only valid target. Store it in step 1; do not rely on window_handles[0] if order matters, since handle order is not guaranteed.
  • Shared driver across threads: Parallel tests using one driver instance can close each other’s windows. WebDriver instances are not thread-safe; use one driver per thread or process.
  • Headless mode differences: Legacy --headless and --headless=new differ in window management and popup handling. If the error appears only headless, test with --headless=new and pin a matching Chrome and ChromeDriver major version.
  • Driver/browser version mismatch: An outdated ChromeDriver mishandles context destruction. Use Selenium 4.6+ with Selenium Manager, or verify with chromedriver --version and google-chrome --version.
  • Firefox: geckodriver raises NoSuchWindowException: Browsing context has been discarded for the same condition. The switch-to-surviving-handle fix applies unchanged.
  • Selenium Grid or remote drivers: Extra latency widens the race window between alert appearance and the next command. Increase the explicit wait timeout instead of adding sleep calls.
  • current_window_handle as a liveness check: It throws when the window is gone. Test liveness with driver.window_handles (never throws for a missing window) or catch NoSuchWindowException around the specific call.
  • Alert triggered inside an iframe: After the window closes, the frame context is also invalid. Call driver.switch_to.default_content() after switching windows.
See also  Cloudflare Turnstile Infinite Loop on Automated Chromium: Headless Bypass Flags

Leave a Comment