{"record":{"id":"0e0acfbc2272a864","repo":"karatelabs/karate","slug":"cause-message-jspromise","errorCode":null,"errorMessage":"<cause message>","messagePattern":"<cause message>","errorType":"exception","errorClass":"io.karatelabs.js.EngineException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/JsPromise.java","lineNumber":248,"sourceCode":"\n    /** {@link #await()} with a deadline; throws {@link EngineTimeoutException}\n     *  if the promise has not settled by then. */\n    public Object join(Duration timeout) {\n        try {\n            return timeout == null\n                    ? toFuture().get()\n                    : toFuture().get(Math.max(timeout.toMillis(), 0), TimeUnit.MILLISECONDS);\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n            throw new EngineInterruptedException();\n        } catch (TimeoutException e) {\n            throw new EngineTimeoutException(\"timed out waiting for promise\");\n        } catch (ExecutionException e) {\n            Throwable cause = e.getCause() == null ? e : e.getCause();\n            if (cause instanceof RuntimeException re) {\n                throw re;\n            }\n            throw new EngineException(cause.getMessage(), cause);\n        }\n    }\n\n    /**\n     * The reverse association that makes a round trip lossless: handing a\n     * promise's own {@link #toFuture()} back into JS recovers the original\n     * {@code JsPromise}, independent of the scope's stage cache.\n     */\n    static final class PromiseView extends CompletableFuture<Object> {\n\n        final JsPromise owner;\n\n        PromiseView(JsPromise owner) {\n            this.owner = owner;\n        }\n    }\n\n    @Override","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/JsPromise.java#L230-L266","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n} catch (EngineException e) { log(e.getMessage()); } // loses root cause\n// after\n} catch (EngineException e) { log(e.getMessage(), e.getCause()); } // root cause visible","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"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; }","preventionTips":["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."],"tags":["promise","wrapped-exception","root-cause"],"backgroundTag":"promise-execution-failed","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}