Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A 200 OK belongs to an HTTP response; WebElement.click() is a separate browser interaction. The request that returned 200 may have loaded the document while the element is hidden, disabled, covered by another painted element, replaced in the DOM, inside a different frame, or not yet ready after JavaScript runs. Selenium clicks the target element’s in-view center, so a covering banner can make the click fail even when every network request looks successful.

Debug the WebDriver exception and page state independently from the network status. Then wait for the condition your test actually needs and verify the post-click result.

What a 200 response does—and does not—prove

HTTP status 200 says that one HTTP request received a successful response. It does not say that Selenium found the intended node, that the node is visible or enabled, that its center is unobscured, or that the application has finished rendering its interactive state. Selenium’s documented interaction model scrolls an element into view, checks whether it can be interacted with, and clicks its center. If another element covers that point, Selenium raises an element-click-intercepted error.

Keep two timelines separate:

  • Network timeline: DNS, requests, responses and status codes.
  • WebDriver timeline: locating a current node, entering the right browsing context, waiting for interactability, dispatching the click and observing the resulting UI state.

A page-load event or a 200 response can occur before a JavaScript framework enables a button, removes a modal, replaces a node, or finishes an asynchronous action.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Identify the failure from the exception

Observable clue Likely condition Next check
ElementClickInterceptedException Another painted element covers the target’s center Inspect the blocking element; close it or wait until it disappears, then use a normal WebDriver click
ElementNotInteractableException or a visibility error The matching node is not usable for pointer interaction Check displayed state, viewport position and whether the control is enabled
StaleElementReferenceException The previously found node was detached or its browsing context changed Locate the element again after navigation, DOM replacement or a frame refresh
No exception, but no business result The command returned before the application reached the expected state Wait for a URL, state change or newly visible element that proves success
Intermittent missing or late element WebDriver raced JavaScript-driven rendering Use a specific expected condition instead of a fixed sleep

A repeatable diagnostic sequence

  1. Capture the exact failure. Record the exception class and message, locator, browser and driver versions, current URL, window handle and screenshot. The message for an intercepted click often identifies the element receiving the click.
  2. Confirm the browsing context. Make sure the test is in the intended window and frame. A locator can be correct for the main document but wrong for iframe content.
  3. Wait for the target state. Prefer visibility, enabled state, frame availability or staleness conditions over an arbitrary delay.
  4. Investigate the click point. For an intercepted click, look for cookie consent panels, newsletter popups, chat widgets, sticky headers, loading masks and animations at the target’s center.
  5. Reacquire after change. Any navigation or front-end rerender can invalidate a stored WebElement. Find it again.
  6. Assert the outcome. A click call completing is not proof that navigation, submission or a state transition completed.

Use explicit waits that match the state

Selenium’s Python expected conditions include visibility, clickability, frame availability and staleness. element_to_be_clickable checks that the element is visible and enabled; it cannot guarantee that a separate overlay will not cover the center a moment later.

from selenium import webdriver
from selenium.common.exceptions import (
    ElementClickInterceptedException,
    ElementNotInteractableException,
    StaleElementReferenceException,
    TimeoutException,
)
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

URL = "https://example.com/checkout"
TARGET = (By.CSS_SELECTOR, "button[type='submit']")
OVERLAY = (By.CSS_SELECTOR, ".cookie-banner, .modal-backdrop, .loading-mask")

options = webdriver.ChromeOptions()
options.add_argument("--window-size=1440,1000")
driver = webdriver.Chrome(options=options)
wait = WebDriverWait(driver, 20, poll_frequency=0.2)

try:
    driver.get(URL)

    # If the site displays a consent control, handle it using its real locator.
    consent = (By.CSS_SELECTOR, "button[data-accept-cookies]")
    try:
        wait.until(EC.element_to_be_clickable(consent)).click()
    except TimeoutException:
        pass                         # No consent control on this run.

    # Wait for a known blocker to become invisible, if that blocker exists.
    try:
        wait.until(EC.invisibility_of_element_located(OVERLAY))
    except TimeoutException:
        raise RuntimeError("An overlay is still present; inspect its selector and state")

    # Locate late, immediately before clicking.
    button = wait.until(EC.element_to_be_clickable(TARGET))
    button.click()

    # Prove the business result, not merely command completion.
    wait.until(EC.url_contains("/confirmation"))
finally:
    driver.quit()

Replace the example selectors with selectors from the page under test. If a locator matches several nodes, make it specific enough to identify the intended control. Do not “fix” an overlay by hiding arbitrary page elements in production tests; close it through the same user-facing control when possible.

Handle frames, navigation and refreshed DOM nodes

Switch into the correct iframe

Elements inside an iframe are not in the parent document’s context. Wait for the frame and switch before locating the control; switch back afterward if the test continues in the parent page.

frame = wait.until(EC.frame_to_be_available_and_switch_to_it(
    (By.CSS_SELECTOR, "iframe[name='payment']")
))
wait.until(EC.element_to_be_clickable((By.ID, "pay"))).click()
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".paid")))
driver.switch_to.default_content()

Re-find after a DOM update

StaleElementReferenceException means the old reference no longer identifies an element in the current DOM. It is not necessarily a bad locator. Navigation, a React/Vue-style rerender, a refreshed iframe or a replacement of the button can all detach the node.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
for attempt in range(3):
    try:
        wait.until(EC.element_to_be_clickable(TARGET)).click()
        break
    except StaleElementReferenceException:
        if attempt == 2:
            raise
        # The next loop locates a fresh node.

Do not retain a WebElement across a known navigation or component update. Retain the locator and resolve it at the point of action.

When clickability still fails

Center-point obstruction

An element can be visible and enabled while a fixed header, modal, tooltip, consent banner or animation occupies its center. Inspect the page at the moment of failure, including a screenshot and the exception’s “other element would receive the click” text. Wait for the blocker to disappear, scroll so the target is not under a sticky header, or interact with the blocker first.

Not interactable is not “not found”

A DOM match may be hidden, off-screen, disabled, or a non-interactive wrapper around the real control. Check the rendered node that a user would click. If the site intentionally enables it only after validation or an API result, wait for that state rather than forcing a click.

Timing races

Use a condition tied to the application: a spinner becomes invisible, a button becomes enabled, a result appears, or a URL changes. Fixed sleeps make fast runs slower and slow runs unreliable. Selenium describes application readiness—not merely page loading—as a common automation challenge.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Diagnose a click that returns without the expected result

A click can dispatch successfully yet fail to complete the business action. Add an assertion for the outcome your user needs:

  • Navigation: wait for a URL fragment or a new document title.
  • Modal action: wait for the modal to become invisible and the resulting panel visible.
  • Form submission: wait for a success message, changed button state or confirmation route.
  • Asynchronous save: wait for the saved indicator, not just the disappearance of a spinner.

If the result never appears, check whether the click hit the intended element, whether validation prevented submission, whether the request failed, and whether the test is still in the correct window or frame. A network 200 for one request does not establish that the later action’s request or UI transition succeeded.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Reliability and performance practices

  • Use one explicit-wait policy with a bounded timeout and short polling interval; choose the timeout from the application’s normal worst case, not from a single fast run.
  • Keep browser, driver and Selenium versions recorded with failures. The Python API documentation identified Selenium 4.49.0 at the time of this guidance; behavior can vary with later releases.
  • Capture HTML, a screenshot, console/network diagnostics and the current URL only when a failure occurs to keep normal runs efficient.
  • Prefer stable attributes intended for testing over deeply nested XPath expressions.
  • After a click that changes the page, discard old references and wait for the new state.
  • Do not replace a real interaction with JavaScript element.click() as a first fix. It can bypass the pointer-interaction problem you need the test to detect.

Or skip the browser setup

If your goal is a clean screenshot rather than an interaction test, ScreenshotNeo makes one request to capture a page. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the page verdict and billing result in X-Page-Verdict and X-Billed headers. It also offers an MCP server for Claude, Cursor and other MCP clients, with take_screenshot, get_page_info and capture_pdf tools.

For the browser-independent call, see the ScreenshotNeo API documentation:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

The same request in Python:

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)

And in Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo includes full-page and element captures, device and viewport controls, PDF output, custom CSS and JavaScript, waits, request blocking, authentication headers and cookies, geolocation, caching, signed links, asynchronous webhooks, bulk capture and a usage API. Every feature is on every plan. The Free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.

Frequently Asked Questions

Can a 200 response come from a different request than the click target’s request?

Yes. A network panel may show 200 for the document, an image, or an API call unrelated to the later WebDriver interaction. Match the request to the action before drawing a conclusion.

Should I always scroll the element to the center before clicking?

Scrolling can help with sticky headers, but it does not remove overlays or guarantee a stable center. Resolve the blocker and wait for the target state first.

Why does the test pass locally but fail in CI?

Different viewport dimensions, timing, browser or driver versions, and rendering speed can change which element covers the center or when JavaScript enables the control. Record those details and use state-based waits.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.