Selenium TimeoutException: How to Fix and Prevent It

Selenium TimeoutException: How to Fix and Prevent It

Selenium throws TimeoutException when a wait condition is not met within the allotted time. The three main types are: element not found within explicit wait timeout (WebDriverWait), page load exceeding driver.timeouts.pageLoad, and JavaScript execution exceeding driver.timeouts.script. Fix by increasing the timeout, using more specific expected conditions, or waiting for the right element/condition.

Key Takeaways

TimeoutException always means "condition not met in time." Selenium waited as long as you told it to, the condition wasn't satisfied, and it gave up. The fix is either: increase the timeout, fix the condition you're waiting for, or fix the application so the element appears when expected.

Never fix TimeoutException with time.sleep(). Adding time.sleep(5) after a timeout is a band-aid that makes tests slow and still flaky. Wait for the specific element or condition you actually need.

presence_of_element_located vs visibility_of_element_located matters. An element can be present in the DOM (attached to the page) but not visible (display:none, opacity:0). If you're clicking an element, use element_to_be_clickable. If you're reading text, use visibility_of_element_located.

Page load timeout is separate from element wait timeout. driver.set_page_load_timeout(30) controls how long Selenium waits for the initial page load. WebDriverWait(driver, 10) controls how long you wait for a specific element. They're independent settings.

Script timeout applies to execute_script() calls. If you run JavaScript via Selenium and it hangs, TimeoutException is thrown after driver.set_script_timeout() seconds. Default is 30 seconds.

What Causes Selenium TimeoutException

TimeoutException is Selenium's way of saying: "I waited as long as you told me to, the condition was never satisfied, I'm giving up."

There are three distinct timeout types in Selenium, each with its own setting and error context:

1. Explicit wait timeout (most common)

from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

try:
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "submit-button"))
    )
except TimeoutException:
    print("Element not found within 10 seconds")

2. Page load timeout

driver.set_page_load_timeout(30)
driver.get("https://slow-site.example.com")  # TimeoutException if > 30s

3. Script execution timeout

driver.set_script_timeout(10)
driver.execute_script("return longRunningFunction()")  # TimeoutException if > 10s

Diagnosing the Cause

When you see TimeoutException, first identify which type it is by reading the full error message and stack trace.

selenium.common.exceptions.TimeoutException: Message:
  Stacktrace:
  ...

The stack trace shows whether it came from WebDriverWait.until(), driver.get(), or driver.execute_script().

Check 1: Is the element actually on the page?

Open your browser's DevTools and manually search for the element you're waiting for.

# What you wrote
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "submit-btn"))
)

# What's actually in the HTML
# <button id="submitBtn">Submit</button>  ← ID is different!

Case sensitivity matters. submit-btn != submitBtn.

Check 2: Is the element inside an iframe?

Selenium can't find elements inside iframes without switching to them first.

# Wrong - element is inside an iframe
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "payment-form"))
)  # TimeoutException

# Right - switch to iframe first
driver.switch_to.frame("payment-iframe")
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "payment-form"))
)

Check 3: Is the element present but not visible?

presence_of_element_located finds elements in the DOM even if they're hidden. visibility_of_element_located requires the element to be visible and have non-zero dimensions.

# Element exists in DOM but is hidden (display:none)
EC.presence_of_element_located((By.ID, "modal"))  # Succeeds — element is there
EC.visibility_of_element_located((By.ID, "modal"))  # TimeoutException — element is hidden

If your element is hidden initially and becomes visible later (like a modal), wait for visibility_of_element_located.

Check 4: Is the element inside a shadow DOM?

Shadow DOM elements require special handling:

# Regular find — won't work for shadow DOM
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.CSS_SELECTOR, "custom-element .inner-div"))
)  # TimeoutException

# Shadow DOM — must use JavaScript
shadow_host = driver.find_element(By.CSS_SELECTOR, "custom-element")
shadow_root = driver.execute_script("return arguments[0].shadowRoot", shadow_host)
element = shadow_root.find_element(By.CSS_SELECTOR, ".inner-div")

How to Fix TimeoutException

Fix 1: Use the Correct Expected Condition

Different situations require different conditions:

from selenium.webdriver.support import expected_conditions as EC

# Element appears in DOM (may still be hidden)
EC.presence_of_element_located((By.ID, "element-id"))

# Element is visible and has dimensions
EC.visibility_of_element_located((By.ID, "element-id"))

# Element is visible AND enabled (for clicks)
EC.element_to_be_clickable((By.ID, "button-id"))

# Element text matches
EC.text_to_be_present_in_element((By.ID, "status"), "Success")

# URL changes (after navigation)
EC.url_contains("dashboard")
EC.url_to_be("https://app.example.com/dashboard")

# Alert appears
EC.alert_is_present()

# Element disappears (loading spinner gone)
EC.invisibility_of_element_located((By.ID, "loading-spinner"))

Choosing the right condition eliminates most TimeoutException errors.

Fix 2: Increase the Timeout Appropriately

If the page or element genuinely takes time to load, increase the timeout:

# Default 10 seconds may be too short for slow networks
WebDriverWait(driver, 30).until(  # Give it 30 seconds
    EC.presence_of_element_located((By.ID, "dashboard"))
)

Do not blindly increase timeouts. If a test is consistently hitting 10 seconds, something is wrong — either with the timeout value, the locator, or the application. Investigate the root cause.

Fix 3: Wait for a More Specific Condition

Instead of waiting for an element to appear, wait for the specific state you need:

# Vague — element might appear before data loads
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.CLASS_NAME, "product-list"))
)

# Better — wait until the list has items
WebDriverWait(driver, 10).until(
    lambda driver: len(driver.find_elements(By.CLASS_NAME, "product-item")) > 0
)

Fix 4: Handle Dynamic Content with Custom Wait Conditions

For complex conditions that built-in expected conditions don't cover:

def wait_for_ajax(driver):
    """Wait until jQuery AJAX calls complete."""
    return driver.execute_script("return jQuery.active == 0")

WebDriverWait(driver, 10).until(wait_for_ajax)
def wait_for_text_not_empty(locator):
    """Wait until element has non-empty text."""
    def condition(driver):
        element = driver.find_element(*locator)
        return element.text.strip() != ""
    return condition

WebDriverWait(driver, 10).until(
    wait_for_text_not_empty((By.ID, "order-total"))
)

Fix 5: Use until_not for Disappearing Elements

Waiting for a loading spinner to disappear:

# Wait for spinner to disappear before interacting
WebDriverWait(driver, 30).until_not(
    EC.presence_of_element_located((By.CLASS_NAME, "loading-spinner"))
)

Handling TimeoutException Gracefully

Sometimes a timeout is expected — you're testing that something does NOT appear:

from selenium.common.exceptions import TimeoutException

def element_appears(driver, locator, timeout=3):
    """Returns True if element appears within timeout, False otherwise."""
    try:
        WebDriverWait(driver, timeout).until(
            EC.presence_of_element_located(locator)
        )
        return True
    except TimeoutException:
        return False

# Test that error message does NOT appear on valid submission
assert not element_appears(driver, (By.CLASS_NAME, "error-message"))

Page Load Timeout vs Element Wait Timeout

These are two separate settings with separate exceptions:

# Page load timeout: how long driver.get() waits
driver.set_page_load_timeout(30)

# Script timeout: how long execute_script() waits
driver.set_script_timeout(10)

# Element wait timeout: set per-wait in WebDriverWait
WebDriverWait(driver, 10).until(...)

A common confusion: the page load timeout fires when driver.get() takes too long (the entire HTML response). The explicit wait fires when an element doesn't appear within the wait period. These are independent.

For slow applications:

driver = webdriver.Chrome()
driver.set_page_load_timeout(60)  # Give slow pages 60 seconds
driver.set_script_timeout(30)     # Give JS execution 30 seconds

Implicit Wait: Why It Causes Problems

Implicit wait is a global setting that makes every element lookup retry for N seconds before throwing NoSuchElementException:

driver.implicitly_wait(10)  # All find_element calls retry for 10 seconds

This seems convenient but causes two problems:

Problem 1: Mixed with explicit waits, behavior is unpredictable

When you have both implicit wait (10s) and explicit wait (5s), Selenium may wait longer than your explicit timeout because the implicit wait keeps retrying internally.

Problem 2: Hides real issues

If a locator is wrong, implicit wait silently retries for 10 seconds before failing. Tests take 10 seconds to fail instead of immediately.

Recommendation: Set implicit wait to 0. Use explicit waits for everything.

driver.implicitly_wait(0)  # Disable implicit wait

# Use explicit waits everywhere
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "element"))
)

Complete Working Example

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

def test_login():
    driver = webdriver.Chrome()
    driver.set_page_load_timeout(30)
    driver.implicitly_wait(0)  # Disable implicit wait

    try:
        # Navigate
        driver.get("https://app.example.com/login")

        # Wait for login form to appear
        WebDriverWait(driver, 10).until(
            EC.visibility_of_element_located((By.ID, "email"))
        )

        # Fill in credentials
        driver.find_element(By.ID, "email").send_keys("user@example.com")
        driver.find_element(By.ID, "password").send_keys("password123")

        # Click submit — wait for it to be clickable
        WebDriverWait(driver, 5).until(
            EC.element_to_be_clickable((By.ID, "login-button"))
        ).click()

        # Wait for redirect to dashboard
        WebDriverWait(driver, 15).until(
            EC.url_contains("/dashboard")
        )

        # Wait for dashboard content to load
        WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.CLASS_NAME, "user-name"))
        )

        print("Login successful")

    except TimeoutException as e:
        print(f"Timeout: {e}")
        driver.save_screenshot("timeout_failure.png")
        raise

    finally:
        driver.quit()

Common TimeoutException Scenarios and Fixes

Scenario Cause Fix
Modal doesn't appear Waiting for wrong element, or trigger not clicked Check the trigger event and use visibility_of_element_located
AJAX data doesn't load Waiting for container, not for data Wait for child elements or non-empty text
Form submit doesn't navigate Form validation error shown instead Check for error messages before asserting redirect
Element in iframe not found Forgot to switch to iframe driver.switch_to.frame() before finding element
Dropdown options not visible Dropdown not opened yet Click the dropdown trigger first, then wait for options
Page reload loops Waiting on wrong URL condition Use EC.url_to_be() with the exact final URL
Dynamic ID in selector ID changes between page loads Use stable attributes like data-testid, CSS class, or text

Summary

TimeoutException means your wait condition wasn't satisfied in time. The most common causes are wrong locators, wrong expected conditions, elements inside iframes, and elements hidden behind loading states.

Fix it by:

  1. Verifying the element selector is correct in DevTools
  2. Using the right expected condition (clickable vs visible vs present)
  3. Waiting for the condition you actually care about (text appears, spinner disappears, URL changes)
  4. Never mixing implicit and explicit waits

Avoid time.sleep() — it's slower and still unreliable.

Read more