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

  1. Increase the page-load timeout configuration to cover slow pages/CI.
  2. Use a lighter wait strategy if full load is unnecessary (e.g. wait until DOMContentLoaded rather than full load).
  3. Block or stub slow third-party resources; check framesStillLoading in the diagnostic to find the offending frame.
  4. If cdpOpen=false in the diagnostic, the browser/websocket crashed — check browser logs and increase browser/memory resources.
  5. 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

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.

Related errors


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)