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
- Return primitive/JSON-friendly values from your JS expression (map to plain objects/strings instead of DOM nodes)
- Wrap the expression in JSON.stringify(...) so the result is a plain string
- Retry the script call shortly after navigation to let Chrome recreate the execution context
- 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
- Return primitives or JSON.stringify'd strings from script()
- Never return raw DOM nodes or deep native object graphs from expressions
- Retry script calls briefly after navigations (context recreation)
- Check the errorMessage in logs for protocol-specific serialization limits
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
- JS exception for: ->
- JS exception
- JS exception
- Do not know how to serialize a BigInt
- Converting circular structure to JSON
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)