karatelabs/karate · error · DriverException

element not found after

Error message

element not found after ${retryCount} retries: ${locator} | ${driverState}

What it means

Before element operations, CdpDriver.retryIfNeeded() polls exists(locator) using the configured retry count and interval. If the element still does not exist after options.getRetryCount() attempts, it throws with the locator and a getDriverState() snapshot for diagnostics.

Solutions

  1. Verify the selector in the browser DevTools against the page state at that step.
  2. Increase options.retryCount / retryInterval in driver config for slow environments.
  3. Wait for prerequisite actions/navigation before the element call, or use driver.waitFor(locator, timeout) for an explicit budget.
  4. Switch to the correct frame first if the element lives in an iframe; inspect the driver-state suffix in the message for the current URL/frame.

Example fix

// before
DriverOptions options = new DriverOptions(); // default retries too low on slow CI
// after
options.setRetryCount(20);
options.setRetryInterval(500);
driver.waitFor('#slow-widget', Duration.ofSeconds(10));
driver.click('#slow-widget');
Defensive patterns

Strategy: retry

Validate before calling

// pre-check existence with your own budget before element ops
long deadline = System.currentTimeMillis() + 10_000;
while (!driver.exists(locator) && System.currentTimeMillis() < deadline) sleep(250);
if (!driver.exists(locator)) throw new IllegalStateException("not present: " + locator);

Try / catch

try {
    driver.click(locator);
} catch (Exception e) {
    if (e.getMessage().contains("element not found after")) {
        // driver state in message shows current URL/frame for triage
        throw new AssertionError("element never appeared: " + e.getMessage(), e);
    } else throw e;
}

Prevention

When it happens

Trigger: Any element API (click, input, text, inputFile, select...) called with a locator that never matches during the retry window: wrong selector, element in an unswitched frame, element removed before the call, or retry budget too short for slow pages.

Common situations: Slow CI machines exceeding the default retry timeout; dynamic content rendered only after an async API call; iframe not switched; typo or changed id/class after an app update; SPA navigation clearing the element between steps.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/853dc0baba39ada7. Report an issue: GitHub.

Appendix: source

Thrown at karate-core/src/main/java/io/karatelabs/driver/cdp/CdpDriver.java:3221

    @SuppressWarnings("unchecked")
    public List<Object> scriptAll(String locator, Object expression) {
        retryIfNeeded(locator);
        String js = Locators.scriptAllSelector(locator, expression);
        return (List<Object>) script(js);
    }

    // ========== Auto-Wait Helper ==========

    /**
     * Auto-wait for element to exist before operations.
     * Uses retryCount and retryInterval from options.
     * This is called automatically before element operations to reduce flaky tests.
     */
    private void retryIfNeeded(String locator) {
        boolean found = retry("element: " + locator, () -> exists(locator));
        if (!found) {
            // Include driver state in exception for better diagnostics
            throw new DriverException("element not found after " + options.getRetryCount() +
                    " retries: " + locator + " | " + getDriverState());
        }
    }

    /**
     * Re-resolve attempts for an element that vanished between the existence check and
     * the action eval. Small on purpose: each attempt already carries a full
     * {@link #retryIfNeeded} poll, and a locator that keeps vanishing is a genuinely
     * unstable page, not a transient worth papering over.
     */
    private static final int ELEMENT_ACTION_ATTEMPTS = 3;

    /**
     * Wait for {@code locator} to exist, then run an action script that re-resolves it
     * inside the page, retrying if it vanished in between.
     * <p>
     * The locator is resolved twice — once here by {@link #retryIfNeeded} (existence)
     * and again by the action JS itself — so a document that swaps between the two evals

View on GitHub (pinned to a22eb90246)