October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
browser automation

How to Fix “Click Succeeded but Load Failed” in Browser Automation

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

A successful click does not prove that navigation finished. A click is an input event; URL changes, document loading, JavaScript hydration, pop-ups, downloads and API calls happen afterward. Fix “click succeeded but load failed” by identifying the expected outcome, waiting for that specific event, and then asserting a URL, response, or stable element. Do not begin by simply increasing a timeout.

Playwright and Selenium use different readiness models. Playwright can wait for navigation milestones such as commit, domcontentloaded and load, while Selenium’s URL-navigation commands use a page-load strategy tied to document.readyState. A click-triggered navigation still needs an outcome-specific wait.

What “click succeeded but load failed” actually means

The message usually describes two separate observations:

  1. The automation driver dispatched the click and the target accepted it.
  2. The next expected browser event was not observed before the framework’s timeout, or the resulting page did not match the test’s expectation.

Those are not contradictory. A server can return an HTTP 503 page successfully, a single-page app can update the DOM without changing the URL, or a click can open a new tab instead of navigating the current page. Conversely, a request can genuinely fail at the network layer. Treat the error as synchronization evidence: determine which event should have happened, then wait for and verify that event.

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

Classify the intended result before changing code

Expected result What to wait for What to assert
Full navigation in the same tab Destination URL or a chosen navigation milestone URL pattern and a stable destination element
SPA route change URL change, route marker, or application-ready element Rendered view and its data, not only readyState
New tab or window Popup/new-page event New page URL and content
Download Download event Suggested filename or saved file
In-page interaction DOM state change, response, or application signal Visible text, attribute, or API result
API-backed action Specific response or UI state HTTP status and resulting UI

Record the current URL immediately before the click and again immediately afterward. That single comparison often reveals that the page never navigated, navigated to an unexpected URL, or opened a different browsing context.

Playwright: wait for the outcome you mean

Playwright waits for initiated navigations in many actions, but a generic click wait is not a substitute for an assertion about the destination. Its navigation API documents the waitUntil milestones and timeout settings: Page API. When more than one navigation is possible, create the URL wait before clicking.

Same-tab navigation to a known URL

import { test, expect } from '@playwright/test';

test('sign-in link reaches the login page', async ({ page }) => {
  await page.goto('https://example.com/account');

  const destination = page.waitForURL('**/login', {
    waitUntil: 'domcontentloaded',
    timeout: 30_000
  });
  await page.getByRole('link', { name: /sign in/i }).click();
  await destination;

  await expect(page).toHaveURL(//login$/);
  await expect(page.getByRole('heading', { name: /log in/i })).toBeVisible();
});

Use load only when all subresources are part of the contract. domcontentloaded is often sufficient for an application whose useful controls appear before images, analytics or third-party frames finish. commit is the earliest milestone: the response has been committed and the document has started loading. Choose the earliest milestone that makes the next assertion reliable, rather than the slowest one by habit.

When the destination is not known in advance

const before = page.url();
const navigation = page.waitForNavigation({
  waitUntil: 'domcontentloaded',
  timeout: 30_000
});
await page.getByRole('button', { name: 'Continue' }).click();
await navigation;

if (page.url() === before) {
  throw new Error(`Click did not navigate; URL is still ${before}`);
}
await page.locator('[data-page-ready="true"]').waitFor();

Prefer waitForURL when the expected route is known. A URL assertion catches redirects to an error page or an authentication wall that a broad navigation wait would otherwise accept.

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

Clicks that open a popup or download

const popup = page.waitForEvent('popup');
await page.getByRole('link', { name: /report/i }).click();
const report = await popup;
await report.waitForLoadState('domcontentloaded');
await expect(report).toHaveTitle(/Report/);

const download = page.waitForEvent('download');
await page.getByRole('button', { name: /export/i }).click();
const file = await download;
await file.saveAs('output.csv');

If you wait for the current page URL while the click actually creates a popup or download, the test will time out even though the click worked.

Selenium: replace implicit assumptions with explicit waits

Selenium’s URL-navigation commands follow the configured page-load strategy. The documented strategies are normal (wait for the full load event), eager (wait for the DOM to be interactive, while other resources may continue), and none (return without waiting): WebDriver options. That setting does not tell Selenium what a click-triggered workflow means. After clicking, wait for the destination URL, a document state, or a stable element.

Python example: URL plus destination element

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

options = webdriver.ChromeOptions()
options.page_load_strategy = "eager"  # normal, eager, or none

driver = webdriver.Chrome(options=options)
wait = WebDriverWait(driver, 30)
try:
    driver.get("https://example.com/account")
    old_url = driver.current_url

    wait.until(EC.element_to_be_clickable((By.LINK_TEXT, "Sign in"))).click()
    wait.until(EC.url_matches(r".*/login/?$"))
    wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "form#login")))

    if driver.current_url == old_url:
        raise AssertionError("The click did not change the URL")
finally:
    driver.quit()

Selenium’s explicit-wait guidance explains that readyState covers HTML and asset loading but not every asynchronous JavaScript update: WebDriver waits. For an SPA, an application-specific element or state is a better readiness signal than document.readyState == 'complete'.

Waiting for document state when it is genuinely the contract

wait.until(lambda d: d.execute_script("return document.readyState") in ("interactive", "complete"))
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "[data-hydrated='true']")))

Use this only when the page exposes a meaningful hydration marker. Otherwise, choose a visible control, heading, row, or status element that proves the requested view is usable.

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

Handle single-page apps and hydration separately

Modern frameworks may render a button before JavaScript attaches its event listener. Playwright warns that a click can be ignored during this hydration gap: Navigations. A DOM node being visible is not proof that its application behavior is ready.

  • Wait for a framework’s ready marker, such as a route container with the expected attribute or text.
  • After clicking, assert the state change the user would see: a new heading, enabled control, row count, or status message.
  • For data-loaded views, wait for the specific response or UI state that consumes it; do not use a fixed sleep as the primary synchronization method.
  • If the control is intermittently ignored, inspect server-rendered versus hydrated markup and move the interaction after the application’s listener-attachment signal.

A client-side route may keep the same document and never fire a traditional navigation event. In that case, waiting for a URL pattern alone is insufficient; combine the route check with a stable element that belongs to the new view.

A repeatable diagnostic workflow

  1. Capture the starting state. Log the current URL, title, window handle, and the target element’s attributes before clicking.
  2. Name the expected outcome. Decide whether it is a URL change, popup, download, in-page state transition, or API response.
  3. Register listeners before the action. Create the Playwright URL, popup, download or response promise before click(); in Selenium, configure the expected condition before or immediately around the click.
  4. Perform one action. Avoid chaining several clicks while diagnosing, because the first unexpected redirect can be hidden by the next command.
  5. Assert a destination. Check the exact URL pattern, a stable element, or the resulting application state.
  6. Inspect network symptoms. Record failed requests and response status. A network failure is different from an HTTP error document.
  7. Check SPA readiness. Verify hydration and asynchronous rendering signals rather than relying only on document readiness.
  8. Tune timeouts last. Adjust navigation or action timeouts only after the expected lifecycle event is correct.

Timeouts, HTTP errors and failed requests are different

A longer timeout cannot repair a wrong URL pattern, an element that never appears, a blocked request, or an application exception. Playwright exposes separate navigation and action timeout configuration in its Page API. Set a deliberate value for the operation you understand, and include the observed URL in failure output.

Do not classify every error page as a network failure. Playwright documents that 404 and 503 responses are successful HTTP responses and therefore do not appear as requestfailed events: BrowserContext API. Assert status explicitly when status matters:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const response = await page.waitForResponse(r => r.url().includes('/api/session'));
await page.getByRole('button', { name: 'Continue' }).click();
expect(response.status()).toBe(200);

For a full navigation, inspect the response returned by the navigation call when available, then check page content for an application error. A 503 error page can load completely while still representing a failed business operation.

Common failure modes and precise fixes

Symptom Likely cause Fix
Click passes, URL never changes The action is an SPA update, a blocked handler, or a non-navigation control. Wait for the changed element or API response; verify hydration and event listeners.
Timeout while waiting for load Slow or permanently pending third-party resources. Use domcontentloaded or commit if those satisfy the test, then assert the required UI.
URL is an unexpected login or error route Redirect, expired session, authorization failure, or server error page. Assert the URL pattern and status; refresh authentication rather than increasing the timeout.
requestfailed is absent for a 404/503 The server returned an HTTP response, so the network request completed. Check response.status() and page content separately.
Current page stays unchanged but a new tab appears The click opened a popup. Wait for the popup/new-page event and switch or assert that page.
Download wait expires The click triggered navigation or an application-generated file instead. Observe the browser event and verify the actual response or file-generation workflow.
Intermittent failures immediately after render Hydration has not attached listeners. Wait for an application-ready marker before clicking.
Selenium proceeds too early eager or none was selected without a follow-up condition. Keep the strategy intentional and add an explicit URL or element wait.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Reliability and performance choices

Use the narrowest reliable condition

A destination heading or route-specific element finishes sooner and proves more than a global sleep. Combine conditions only when each represents a real requirement—for example, URL plus a visible form. Avoid polling for arbitrary milliseconds; fixed delays add latency and still fail under slower conditions.

Keep navigation and action budgets separate

Navigation can legitimately need more time than locating a button, while an element wait should fail quickly when the wrong page is open. Configure each budget at the operation or test level, log the effective values, and include the last URL and title in timeout diagnostics.

Make retries diagnostic, not masking

A retry can handle transient infrastructure problems, but it should preserve a trace, screenshot, console log and network record from the first failure. Repeating a click without resetting the page can submit a form twice or create multiple tabs. Restart from a known URL and session state when retrying.

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.

Or skip the browser setup

If your goal is a dependable screenshot rather than an interactive test, ScreenshotNeo makes one GET request and returns a PNG, JPEG, WebP or PDF. Before capture it accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

Using the API requires an access key. The complete option reference is in the ScreenshotNeo documentation.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

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}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const data = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', data));

ScreenshotNeo also supports full-page captures with lazy images loaded, CSS-selector element shots, dark mode, 12 device presets or custom viewports, retina scale, PDF paper and page-range controls, custom CSS and JavaScript, clicks before capture, selector or network-idle waits, ad/tracker/request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. An MCP server provides take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients.

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing gives two months free, and every feature is available on every plan. Create a free ScreenshotNeo account to try it without a card.

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

Frequently Asked Questions

Is “load failed” a standard browser exception?

No. It is commonly a framework or test-runner diagnosis that combines a completed click with a missing or incorrect follow-up condition. Inspect the framework’s actual timeout and the browser events recorded around the click.

Should I use one global page-load strategy for every Selenium test?

No. Choose the strategy that matches the application, then add explicit waits for the state each test needs. A faster strategy without a follow-up condition can make tests less reliable.

What evidence should a bug report include?

Include the pre-click and post-click URLs, window handles, target selector, expected outcome, timeout setting, response statuses, failed-request details, console errors and a screenshot or trace at failure.

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.

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

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Read next

Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.