karatelabs/karate · error · DriverException
locator failed twice
Error message
locator failed twice: ${locator} What it means
In W3cDriver's locator resolution, when the first (and retry) attempt to find the element via injected JavaScript fails with an exception, the driver wraps the second failure in a DriverException 'locator failed twice: <locator>' preserving the cause. It indicates the locator lookup script itself errored twice, not merely that the element was absent.
Solutions
- Fix the locator string syntax so the injected lookup JS does not throw (validate against supported locator formats)
- Ensure the page has fully loaded and the correct frame/window is active before locating
- Increase implicit waits / use waitFor before interacting, and re-locate after navigations
- Inspect the wrapped cause (e2) in logs for the real script error (e.g. 'document is not defined', CSP violations)
Example fix
// before
driver.click("//input[@nme='username']"); // typo causes JS lookup failure twice
// after
driver.waitFor("//input[@name='username']").click(); Defensive patterns
Strategy: retry
Validate before calling
// sanity-check locator before use
Objects.requireNonNull(locator);
if (locator.isBlank()) throw new IllegalArgumentException("empty locator"); Try / catch
try {
driver.click(locator);
} catch (Exception e) {
if (e.getMessage().contains("locator failed twice")) {
driver.reload(); // recover page context, then retry once
driver.click(locator);
}
} Prevention
- Wait for page readiness before locating after navigations
- Validate locator syntax against supported formats
- Log the wrapped cause to diagnose script-level failures
When it happens
Trigger: Calling find/locator APIs (driver.locate, waitFor, click, input on a stale/invalid locator string) where executeScript of the locator JS throws on both attempts — e.g. malformed locator syntax, the __kjs runtime not injected, or a navigation/page context invalidation mid-lookup.
Common situations: Typo'd or unsupported wildcard locator syntax; page navigated or frame changed between attempts causing JS execution errors; browser extension or CSP blocking injected script; clicking too fast after navigation so the execution context is destroyed.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- Element not found
- bad wildcard locator
- Could not find active element for keyboard input
- Dialog callback handler not supported on WebDriver backend…
- element not found after
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/c7ae3d152a0cc3df.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/driver/w3c/W3cDriver.java:1047
// v1 pattern: single retry on locator failure — handles transient DOM changes
String js = "return " + Locators.selector(locator);
try {
Object result = session.executeScript(js);
if (W3cSession.isElementReference(result)) {
return W3cSession.elementIdFrom(result);
}
} catch (Exception e) {
logger.warn("locator failed, will retry once: {}", e.getMessage());
}
// Single retry after sleep
sleep(options.getRetryInterval());
try {
Object result = session.executeScript(js);
if (W3cSession.isElementReference(result)) {
return W3cSession.elementIdFrom(result);
}
} catch (Exception e2) {
throw new DriverException("locator failed twice: " + locator, e2);
}
throw new DriverException("Element not found: " + locator);
}
/**
* Inject the Karate JS runtime (__kjs) into the browser if not already present.
* Same pattern as CdpDriver — loads driver.js from classpath resources.
* Provides wildcard locator resolution, shadow DOM traversal, and shared utilities.
*/
private void ensureKjsRuntime() {
try {
// Guard on __kjs.resolve (the wildcard resolver), not merely __kjs — a co-installed
// helper may seed a partial window.__kjs without it; driver.js extends, never clobbers.
Object exists = session.executeScript(
"return typeof window.__kjs !== 'undefined' && typeof window.__kjs.resolve === 'function'");
if (!Boolean.TRUE.equals(exists)) {
session.executeScript(DRIVER_JS);
logger.debug("Injected __kjs runtime into browser");View on GitHub (pinned to a22eb90246)