karatelabs/karate · error · RuntimeException

JS error

Error message

JS error: {error}

What it means

A Runtime.evaluate call returned an error response (not an exception in page JS, but a CDP-level error such as an unserializable result or an internal evaluation failure). Karate logs a warning with a truncated expression and rethrows 'JS error: <error>'. 'Object reference chain is too long' is special-cased and returns null instead.

Solutions

  1. Return primitive/JSON-friendly values from your JS expression (map to plain objects/strings instead of DOM nodes)
  2. Wrap the expression in JSON.stringify(...) so the result is a plain string
  3. Retry the script call shortly after navigation to let Chrome recreate the execution context
  4. If it persists, upgrade Karate/browser — some CDP serialization quirks are version-specific

Example fix

// before
Object huge = driver.script("document.querySelectorAll('a')"); // JS error
// after
String json = (String) driver.script("JSON.stringify(Array.from(document.querySelectorAll('a')).map(a => a.href))");
Defensive patterns

Strategy: try-catch

Validate before calling

// keep evaluate results serializable by design: return strings/numbers/plain objects
String safeExpr = "JSON.stringify((" + expression + "))";

Try / catch

try { Object v = driver.script(expr); } catch (RuntimeException e) { if (e.getMessage().startsWith("JS error:")) { return null; /* e.g. unserializable */ } throw e; }

Prevention

When it happens

Trigger: driver.script(expression) where the CDP response carries errorMessage — deep/deeply-nested return values the protocol cannot serialize, evaluating on a torn-down execution context reported as an error, or browser refusing the evaluate request.

Common situations: Scripts returning DOM nodes or huge object graphs; evaluating on a stale context right after navigation; calling script() while the page is mid-navigation; expressions referencing objects Chrome cannot remote-object serialize.

Related errors


AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/287064c8c8060e3b. Report an issue: GitHub.

Appendix: source

Thrown at karate-core/src/main/java/io/karatelabs/driver/cdp/CdpDriver.java:2013

        // For OOPIFs, cdp.sessionId is routed to the iframe's own CDP session. Each session
        // has its own "default" execution context, which IS the OOPIF's main world. Passing
        // a contextId from frameContexts (registered against a different session) would yield
        // "Cannot find context with specified id". Returning null lets CDP pick the default.
        if (oopifSessions.containsKey(currentFrame.id)) {
            return null;
        }
        return frameContexts.get(currentFrame.id);
    }

    private Object extractJsValue(CdpResponse response, String expression) {
        if (response.isError()) {
            String message = response.getErrorMessage();
            if (message != null && message.contains("Object reference chain is too long")) {
                logger.debug("script result not serializable, returning null: {}", truncate(expression, 100));
                return null;
            }
            logger.warn("JS error for expression: {}", truncate(expression, 100));
            throw new RuntimeException("JS error: " + response.getError());
        }
        Object exceptionDetails = response.getResult("exceptionDetails");
        if (exceptionDetails != null) {
            // Try to get detailed error message from exception object
            String description = response.getResultAsString("exceptionDetails.exception.description");
            if (description != null && !description.isEmpty()) {
                logger.warn("JS exception for: {} -> {}", truncate(expression, 100), truncate(description, 200));
                throw new RuntimeException("JS exception: " + description);
            }
            // Fall back to text (usually just "Uncaught")
            String text = response.getResultAsString("exceptionDetails.text");
            logger.warn("JS exception for: {} -> {}", truncate(expression, 100), text);
            throw new RuntimeException("JS exception: " + text);
        }
        Object value = response.getResult("result.value");
        logger.trace("script result: {}", value);
        return value;
    }

View on GitHub (pinned to a22eb90246)