karatelabs/karate · error · io.karatelabs.js.EngineException
<cause message>
Error message
<cause message>
What it means
When the promise's underlying future completes with ExecutionException, join unwraps the cause and rethrows it: RuntimeExceptions directly, anything else as EngineException(cause.getMessage(), cause). The message "<cause message>" is therefore whatever the original failure produced — this error is a wrapper, and the root cause is the real diagnosis target.
Solutions
- Inspect the cause chain (getCause) — fix the root exception, not the wrapper.
- If the cause is a JS error, run the failing script standalone to see the original stack.
- Ensure async tasks catch and convert their failures into promise rejections with meaningful messages.
- Log the full EngineException with its cause rather than only the top-level message.
Example fix
// before
} catch (EngineException e) { log(e.getMessage()); } // loses root cause
// after
} catch (EngineException e) { log(e.getMessage(), e.getCause()); } // root cause visible Defensive patterns
Strategy: try-catch
Try / catch
try { return p.join(); } catch (EngineException e) { Throwable root = e; while (root.getCause() != null) root = root.getCause(); log.error("promise failed, root cause: " + root, root); throw root instanceof RuntimeException re ? re : e; } Prevention
- Always inspect the cause chain — this error is only a wrapper.
- Convert failures inside async tasks into explicit promise rejections with descriptive messages.
- Catch and log full stack traces, not just the top-level message.
- Add tests covering failure paths of awaited async operations.
When it happens
Trigger: The async computation behind the promise threw inside the executor thread: a JS runtime error during an awaited task, an exception in host callback code, or any checked exception wrapped by the future.
Common situations: JS script errors surfacing through await; NPEs or IO errors thrown by host-implemented async tasks; executor rejected/failed tasks; version changes that alter an underlying API the async task calls.
Related errors
- Token refresh failed:
- await on a promise that can never settle
- <cause message>
- <interrupted>
- timed out waiting for promise
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/0e0acfbc2272a864.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/js/JsPromise.java:248
/** {@link #await()} with a deadline; throws {@link EngineTimeoutException}
* if the promise has not settled by then. */
public Object join(Duration timeout) {
try {
return timeout == null
? toFuture().get()
: toFuture().get(Math.max(timeout.toMillis(), 0), TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new EngineInterruptedException();
} catch (TimeoutException e) {
throw new EngineTimeoutException("timed out waiting for promise");
} catch (ExecutionException e) {
Throwable cause = e.getCause() == null ? e : e.getCause();
if (cause instanceof RuntimeException re) {
throw re;
}
throw new EngineException(cause.getMessage(), cause);
}
}
/**
* The reverse association that makes a round trip lossless: handing a
* promise's own {@link #toFuture()} back into JS recovers the original
* {@code JsPromise}, independent of the scope's stage cache.
*/
static final class PromiseView extends CompletableFuture<Object> {
final JsPromise owner;
PromiseView(JsPromise owner) {
this.owner = owner;
}
}
@OverrideView on GitHub (pinned to a22eb90246)