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

  1. Avoid evaluating JS immediately after non-document navigations (downloads/204) — the default-context fallback may return unexpected values.
  2. Re-navigate to a real document before evaluating, restoring a proper main-frame context.
  3. Increase retries/interval in driver config if the context is merely slow to register.
  4. 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

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


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)