karatelabs/karate · warning
navigation aborted by browser, current document retained
Error message
navigation aborted by browser, current document retained: {} What it means
After all Page.navigate retry attempts return net::ERR_ABORTED, the driver concludes the browser deliberately retained the current document (download, 204/205 response, or window.stop()) and logs this warning instead of waiting for a load event that will never fire. The method returns normally with the page as-is; it does not throw. Loader-bound waits are skipped because the returned loader never commits and DOMContentLoaded would never arrive.
Solutions
- Use Karate HTTP steps or driver.download for URLs that return files or 204/205 instead of documents.
- Remove or condition window.stop() in the page under test.
- Verify the URL actually serves an HTML document (curl -I to check Content-Disposition/status).
- If you expect a document but see this, retry the navigation; a racing reset may have aborted it.
Example fix
// before
driver.get('https://host/api/export'); // 204 or download, navigation aborted
// after
karate.call('classpath:helpers/download.feature'){ // use HTTP/download flow for non-document responses
def resp = karate.http('https://host/api/export').get();
} Defensive patterns
Strategy: fallback
Validate before calling
var resp = karate.http(url).get(); if (resp.status == 204 || resp.header('content-disposition') != null) { /* use HTTP flow, not driver.get */ } Prevention
- Use karate HTTP steps for downloads and 204/205 endpoints
- Verify Content-Type is a document before driver.get()
- Remove window.stop() from test pages
- Re-navigate to a real document afterwards if you need DOM access
When it happens
Trigger: All navAttempts of driver.get()/loadUrl() end with errorText 'net::ERR_ABORTED', so the 'aborted' flag is still true after the retry loop.
Common situations: Navigating to a CSV/PDF link that downloads instead of rendering, endpoints returning 204 No Content, SPA code calling window.stop() during load, or an external link intercepted by a service worker.
Related errors
- Page.navigate returned ERR_ABORTED, retrying
- page load timeout after
- Page.navigate timed out, retrying
- page load timeout after %dms - url
- readyState check exception
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/d00f850a45c33eb0.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/driver/cdp/CdpDriver.java:1324
throw e;
}
aborted = "net::ERR_ABORTED".equals(navResponse.getResultAsString("errorText"));
if (!aborted) {
break; // committed, an error page under the same loader, or a normal response
}
if (attempt < navAttempts - 1) {
logger.warn("Page.navigate returned ERR_ABORTED, retrying ({}/{}): {}", attempt + 1, navAttempts - 1, url);
sleep(150); // let an in-flight/racing navigation (e.g. pooled-reset about:blank) settle
}
}
// Every attempt aborted — a deliberate download / 204-205 / window.stop() that
// retains the current document. The returned loader never commits, never fires
// DOMContentLoaded, and a loader-bound wait for it could only end in a timeout,
// so return with the page as-is (genuine load failures instead commit an error
// page under the SAME loader, and the normal wait below handles those).
if (aborted) {
logger.warn("navigation aborted by browser, current document retained: {}", url);
pendingNavigationUrl = null;
return;
}
// data: and about: URLs commit locally and don't fire the normal load lifecycle,
// so the full waitForPageLoad below would only ever time out on them. Returning
// with no barrier at all is what let the pooled reset's about:blank still be in
// flight when the scenario's first real navigation was issued: Chrome aborted one
// against the other (net::ERR_ABORTED clustered on the post-reset /input, /wait,
// /shadow-dom navigations) and the scenario continued against whichever document
// won that race. Barrier on THIS navigation actually committing instead — cheap,
// since a local document commits in milliseconds.
//
// Best-effort and bounded: if the commit signal never arrives we proceed anyway,
// which is no worse than the unconditional return this replaces.
if (url.startsWith("data:") || url.startsWith("about:")) {
String localLoaderId = navResponse.getResultAsString("loaderId");
if (localLoaderId != null && !pollUntil(LOCAL_NAV_COMMIT_TIMEOUT_MS, 10,View on GitHub (pinned to a22eb90246)