ScreenshotNeo

BlogHow-to

Why Multiple IEDriverServer Processes Remain After Selenium Test Failures

Learn why IEDriverServer.exe survives failed Selenium tests and how to prevent, detect, and clean up orphaned driver and browser processes on Windows.

By the ScreenshotNeo team30 September 20266 min read

Why Multiple IEDriverServer Processes Remain After Selenium Test Failures

Multiple IEDriverServer.exe processes usually mean teardown did not run on every failure path, session startup failed before a driver object existed, or child processes were not reaped. The reliable fix is to make teardown deterministic, track the driver service process separately, and verify the entire process tree after cleanup.

Selenium documents that simultaneous Internet Explorer Driver instances are largely untested and that running IEDriverServer.exe as a Windows Service is expressly unsupported. Treat both configurations as risk factors while diagnosing leaks.

What is actually left running?

An Internet Explorer test normally involves several processes:

  • Your test runner (for example, pytest, NUnit, or a CI worker).
  • IEDriverServer.exe, which accepts WebDriver commands.
  • iexplore.exe browser processes and helper processes.

driver.quit() requests session shutdown, but it cannot run if session creation failed before a driver object was instantiated. Even a successful quit() can leave descendants behind when the driver or browser fails to terminate cleanly. Selenium issue #15632 describes startup failure without a usable driver object and zombie browser children; issue #10863 covers orphan-driver behaviour and CI timeouts.

The failure paths that create multiple processes

1. Cleanup was skipped

An assertion, timeout, process kill, or exception in setup can bypass code placed after the test body. Put cleanup in the framework’s guaranteed teardown hook or a language-level finally block.

Track the service PID and reap its browser descendants when a session fails.
Track the service PID and reap its browser descendants when a session fails.

2. Startup failed before driver existed

If the service starts and then the session handshake fails, Selenium may never return a driver object. Code such as if driver: driver.quit() cannot clean up a service that was created internally and is no longer referenced. Keep the service object and its PID before constructing the driver.

3. Descendants were not reaped

The driver can exit while an Internet Explorer child remains, or the browser can hold a handle that delays process exit. Verify both the driver process and its descendants instead of assuming that a successful return from quit() means the tree is gone.

4. Unsupported execution context

Selenium’s IE documentation says: “Attempting to use IEDriverServer.exe as part of a Windows Service application is expressly unsupported.” The same documentation says simultaneous instances are possible in theory but “largely untested,” with possible cookie and window-focus issues. See the Selenium documentation before relying on either model.

A deterministic cleanup pattern in Python

This example records the service PID, always attempts quit(), and then checks the Windows process tree. It handles both normal test failures and session-start failures.

import os
import time
import psutil
from selenium import webdriver
from selenium.webdriver.ie.service import Service


def descendants(pid):
    try:
        parent = psutil.Process(pid)
        return parent.children(recursive=True)
    except psutil.Error:
        return []


def stop_tree(pid, grace_seconds=5):
    """Terminate descendants first, then the service; verify what remains."""
    try:
        root = psutil.Process(pid)
    except psutil.Error:
        return []

    children = descendants(pid)
    for proc in reversed(children):
        try:
            proc.terminate()
        except psutil.Error:
            pass
    try:
        root.terminate()
    except psutil.Error:
        pass

    _, alive = psutil.wait_procs(children + [root], timeout=grace_seconds)
    for proc in alive:
        try:
            proc.kill()
        except psutil.Error:
            pass
    return [p.pid for p in alive if p.is_running()]


service = Service(executable_path=r"C:\tools\IEDriverServer.exe")
driver = None
service_pid = None
try:
    service.start()
    service_pid = service.process.pid if service.process else None
    driver = webdriver.Ie(service=service)
    driver.get("https://example.com")
    # assertions and test actions go here
finally:
    if driver is not None:
        try:
            driver.quit()
        except Exception as exc:
            print(f"quit failed: {exc!r}")
    if service_pid is not None:
        remaining = stop_tree(service_pid)
        if remaining:
            print(f"processes still alive after cleanup: {remaining}")
    time.sleep(0.5)

Install the process-inspection dependency with python -m pip install selenium psutil. The short delay is environment-specific; it gives Windows time to release handles before the next test starts.

Guaranteed teardown with pytest

import pytest
from selenium import webdriver

@pytest.fixture
def ie_driver():
    driver = None
    try:
        driver = webdriver.Ie()
        yield driver
    finally:
        if driver is not None:
            try:
                driver.quit()
            except Exception:
                # Log the exception; do not hide the original test failure.
                pass

For a service PID that must also be cleaned after startup failure, create and start the Service explicitly as shown in the previous section, then add its PID cleanup to the fixture’s finally block.

Equivalent pattern in C#

using OpenQA.Selenium;
using OpenQA.Selenium.IE;

IWebDriver driver = null;
try
{
    driver = new InternetExplorerDriver();
    driver.Navigate().GoToUrl("https://example.com");
}
finally
{
    if (driver != null)
    {
        try { driver.Quit(); }
        catch (WebDriverException) { /* log and continue cleanup */ }
        finally { driver.Dispose(); }
    }
}

If the constructor throws, no driver object exists. In that case, use a separately managed service process and terminate it from the catch/finally path, then inspect descendants with Windows process tools.

Process-tree cleanup should target only the processes owned by the test run.
Process-tree cleanup should target only the processes owned by the test run.

How to find and remove orphaned processes on Windows

Inspect by name

Get-Process IEDriverServer, iexplore -ErrorAction SilentlyContinue |
  Select-Object Id, ProcessName, StartTime, Path

Inspect the parent-child tree

Get-CimInstance Win32_Process |
  Where-Object { $_.Name -in @('IEDriverServer.exe','iexplore.exe') } |
  Select-Object ProcessId, ParentProcessId, Name, CommandLine

Terminate a known test-owned tree

taskkill /PID 12345 /T /F

Use the PID captured for the current test run. Avoid killing every Internet Explorer process on a shared machine because another desktop session may own it.

Startup-failure handling checklist

  1. Start the driver service explicitly and record its PID.
  2. Attempt to construct the WebDriver session.
  3. Call quit() only when a driver object was returned.
  4. Stop the recorded service PID when construction fails.
  5. Enumerate descendants and verify that IEDriverServer.exe and test-owned iexplore.exe processes have exited.
  6. Save driver logs, PIDs, command lines, and timestamps with the failed test.

Common errors and fixes

Symptom Likely cause Fix
driver is undefined in teardown Constructor failed before assignment Initialize it to None/null and track the service independently.
quit() returns but processes remain Child browser or handle did not exit Inspect the process tree, wait briefly, then terminate only the test-owned descendants.
Many drivers appear after parallel tests Concurrent IE instances are weakly tested Run one IE session per isolated desktop/worker and validate cookies and focus; reduce parallelism while diagnosing.
Leaks occur only in CI Service context, permissions, timeouts, or forced worker termination Run as an interactive desktop process, capture service PIDs, and ensure CI cancellation invokes teardown.
Driver starts then immediately fails Underlying browser-driver problem Reproduce with another browser. Selenium troubleshooting notes that many apparent Selenium errors originate in the browser driver.
Windows service deployment is unstable Unsupported execution model Do not run IEDriverServer.exe as a Windows Service; use a supported desktop process.

Reliability and performance considerations

  • Prefer one IE session per worker. Parallel IE-driver instances are largely untested, so isolation is more valuable than maximum concurrency.
  • Use bounded waits. A short post-quit wait can help Windows release handles, but it is a workaround for particular environments, not a universal cure.
  • Keep logs per attempt. Record service PID, browser PID, driver version, command line, start time, quit exception, and remaining processes.
  • Clean between tests. A leaked browser can retain profiles, cookies, window focus, or locks that make later failures misleading.
  • Do not hide the first exception. Log teardown failures while preserving the assertion or startup error that caused the test to fail.

Or skip the browser setup

If your goal is to capture a page for a test artifact, visual diff, or bug report rather than drive Internet Explorer itself, ScreenshotNeo returns a screenshot or PDF from one request. Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf.

See the ScreenshotNeo API documentation for all options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

FAQ

Does driver.quit() always kill IEDriverServer?

No. It is the normal cleanup operation, but it cannot run without a driver object and it may not reap every child process.

Should I call driver.close() instead?

No. close() closes the current window. Use quit() for session cleanup.

Can I safely kill every IEDriverServer.exe on the machine?

Only on a dedicated test host where you know no other run is active. On shared hosts, terminate the PID and descendants recorded for your test.

Is a Windows service a good way to keep the driver alive?

No. Selenium expressly documents that running IEDriverServer.exe as a Windows Service is unsupported.