karatelabs/karate · error
page load timeout after %dms - url
Error message
page load timeout after %dms - url: %s, strategy: %s, domContentEventFired: %s, framesStillLoading: %s, mainFrameId: %s, expectedLoaderId: %s, supersededLoaderId: %s, committedLoaderId: %s, domContentLoaderId: %s, readyState: %s, jsExec: %s, cdpOpen: %s
What it means
This is the page-load timeout failure: waitForPageLoad exceeded its timeout budget, and the driver throws a RuntimeException carrying a rich diagnostic string (loader IDs, framesStillLoading, readyState, jsExec, cdpOpen). It means DOMContentLoaded/load never completed to the driver's satisfaction within the configured timeout for the given navigation strategy.
Solutions
- Increase the page-load timeout configuration to cover slow pages/CI.
- Use a lighter wait strategy if full load is unnecessary (e.g. wait until DOMContentLoaded rather than full load).
- Block or stub slow third-party resources; check framesStillLoading in the diagnostic to find the offending frame.
- If cdpOpen=false in the diagnostic, the browser/websocket crashed — check browser logs and increase browser/memory resources.
- Navigate directly to the failing URL in a manual browser to identify what never finishes loading.
Example fix
// before
karate.configure('timeout', 10000); // slow iframe exceeds budget
// after
karate.configure('timeout', 45000); // and inspect framesStillLoading in the diagnostic for the culprit frame Defensive patterns
Strategy: retry
Validate before calling
// pre-check that the URL serves a document quickly
var resp = karate.http(url).get(); karate.log('status:', resp.status, 'time:', resp.time); Try / catch
try { driver.get(url); } catch (RuntimeException e) { if (e.getMessage().startsWith("page load timeout")) { karate.log(e.getMessage()); /* parse framesStillLoading/jsExec for diagnosis */ } else { throw e; } } Prevention
- Set realistic page-load timeouts for CI hardware
- Block/stub slow third-party resources
- Inspect the diagnostic's framesStillLoading to find never-ending frames
- Watch cdpOpen in the diagnostic for browser crashes; ensure adequate memory
When it happens
Trigger: driver.get()/loadUrl() where, after timeout.toMillis(), the condition (domContentEventFired + no frames still loading + verified JS execution for the strategy) is not met; e.g. domContentEventFired=false, framesStillLoading non-empty, or jsExec=false.
Common situations: Slow external resources (ads, fonts, analytics) blocking load events, an iframe that never stops loading, page-load timeout configured too low for CI, hung websocket (cdpOpen=false indicates the CDP connection died), or JS context never registering.
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.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- page load timeout after
- {diagnostic: frame switch failed with child-frame url list}
- Page.navigate timed out, retrying
- Page.navigate returned ERR_ABORTED, retrying
- navigation aborted by browser, current document retained
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/1d24bbf9ff1a7474.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/driver/cdp/CdpDriver.java:1440
} catch (Exception e) {
// timeout (or exceptional completion): fall through and re-check
}
}
// Build diagnostic message with comprehensive state info
String url = pendingNavigationUrl != null ? pendingNavigationUrl : "(unknown)";
String readyStateInfo = getReadyStateForDiagnostic();
String jsExecInfo = getJsExecStateForDiagnostic();
String diagnostic = String.format(
"page load timeout after %dms - url: %s, strategy: %s, " +
"domContentEventFired: %s, framesStillLoading: %s, mainFrameId: %s, " +
"expectedLoaderId: %s, supersededLoaderId: %s, committedLoaderId: %s, domContentLoaderId: %s, " +
"readyState: %s, jsExec: %s, cdpOpen: %s",
timeout.toMillis(), url, strategy,
domContentEventFired, framesStillLoading, mainFrameId,
expectedLoaderId, supersededLoaderId, committedLoaderId, domContentLoaderId,
readyStateInfo, jsExecInfo, cdp.isOpen());
logger.warn(diagnostic);
throw new RuntimeException(diagnostic);
}
/**
* Verify that JS execution works in the context script() will actually use.
* <p>
* The page is only truly "loaded" once the main frame's default execution context
* is live - that is the single thing the readiness future tracks. We await it
* briefly (it is usually already complete; during a navigation swap it settles in
* milliseconds) and probe THAT context.
* <p>
* Crucially, an <i>error</i> from the explicit-context probe is NOT taken as "JS not
* ready": {@link #mainContextReady} can hand out a contextId that a loader
* replacement has already torn down when the matching executionContextsCleared was
* never delivered (routine under CI load). The stale id then errors forever and, on
* a fully-loaded page, wedges waitForPageLoad() to a 30s timeout — observed in CI as
* "page load complete but JS context not ready yet" while the timeout diagnostic
* reports {@code jsExec: ok} (it probes the default context). So on an explicit-View on GitHub (pinned to a22eb90246)