karatelabs/karate · warning
main-frame context never became ready after
Error message
main-frame context never became ready after {} attempts, falling back to default context: {} What it means
When retries are exhausted and the main frame's execution context never registered (mainContextUnresolved), the driver degrades gracefully: it logs this warning and sends the Runtime.evaluate on CDP's default context instead of hard-failing. This matches cases like downloads/204/aborted navigations that clear the context without a replacement. Results from the default context can differ from the main-frame context, so the warning exists to make recurrences visible in CI logs.
Solutions
- Avoid evaluating JS immediately after non-document navigations (downloads/204) — the default-context fallback may return unexpected values.
- Re-navigate to a real document before evaluating, restoring a proper main-frame context.
- Increase retries/interval in driver config if the context is merely slow to register.
- Treat this warning as a signal: investigate why the main context never came back (check for ERR_ABORTED in nearby logs).
Example fix
// before
driver.get('https://host/export'); // aborts/download, context never returns
driver.script("document.title"); // falls back to default context, wrong scope
// after
driver.get('https://host/index.html'); // re-establish a real document
String title = driver.script("document.title"); Defensive patterns
Strategy: fallback
Validate before calling
// only evaluate when a real document is loaded
if (driver.url().startsWith("http") && !driver.url().equals("about:blank")) { driver.script("document.title"); } Prevention
- Don't evaluate JS after download/204/aborted navigations
- Re-navigate to a document to restore the main-frame context
- Increase eval retries if the context is merely slow
- Investigate this warning when it recurs — default-context results may be wrong
When it happens
Trigger: script()/eval where after maxRetries getFrameContext() still returns null — e.g. evaluating after a navigation that aborted, a download, or a 204 response wiped the execution context.
Common situations: Evaluating JS right after navigating to a download link or 204 endpoint, context lost after window.stop(), SPA teardown destroying the context, CI environments with slow context re-registration.
Related errors
- currentTargetId fallback to getPages()[0]
- page load complete but JS context not ready yet
- main-frame context not ready, retry
- timeout waiting for frame context: frameId=
- CDP connection failed readiness check
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/f95495b95ac74947.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/driver/cdp/CdpDriver.java:1884
// returns a misleading null instead of the main-frame value - the source
// of a rare CI flake (frame.feature "Multiple switches" reading
// window.mainValue as null under a slow context swap). Self-heal instead:
// give the replacement executionContextCreated more time via the retry
// loop, which re-awaits the readiness future on the next getFrameContext().
// Logged at WARN so a recurrence is visible in CI for the next investigation.
logger.warn("main-frame context not ready, retry {}/{}: {}", attempt + 1, maxRetries, truncate(expression, 100));
sleep(transientInterval);
continue;
}
if (contextId != null) {
message.param("contextId", contextId);
} else if (mainContextUnresolved) {
// Retries exhausted and the main context never registered - e.g. a
// download / 204 / aborted navigation that clears the context without a
// replacement. Degrade to CDP's default context as a last resort rather
// than hard-failing, matching the documented graceful-degradation intent,
// but log loudly so a recurrence stands out in CI logs.
logger.warn("main-frame context never became ready after {} attempts, falling back to default context: {}", maxRetries, truncate(expression, 100));
}
CdpResponse response;
try {
response = message.send();
} catch (DialogOpenedException e) {
// The script itself triggered a blocking JS dialog (alert/confirm/
// prompt/beforeunload). Chrome suspends Runtime.evaluate until the
// dialog is handled, and our Page.javascriptDialogOpening handler
// cancels the pending eval to avoid a 30s CDP timeout.
//
// From the user's perspective this is success, not failure — the
// script did exactly what they asked. The dialog is captured and
// accessible via getDialog() / getDialogText() / driver.dialogText
// and can be resolved with dialog(true|false). Return an empty
// response so extractJsValue yields null.
logger.debug("script opened a dialog, returning null: {}", truncate(expression, 100));
return new CdpResponse(Map.of());View on GitHub (pinned to a22eb90246)