{"record":{"id":"feadbaacb5d6e1d2","repo":"karatelabs/karate","slug":"interrupted-jspromise","errorCode":null,"errorMessage":"<interrupted>","messagePattern":"<interrupted>","errorType":"exception","errorClass":"io.karatelabs.js.EngineInterruptedException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/JsPromise.java","lineNumber":240,"sourceCode":"     * Block until settled and return the fulfillment value, throwing\n     * {@link JsRejectionException} on rejection. For Java callers only — calling\n     * this from the engine thread would wait for a queue that nothing is\n     * pumping.\n     */\n    public Object await() {\n        return join(null);\n    }\n\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","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/JsPromise.java#L222-L258","documentation":"JsPromise.join blocks the calling thread on the promise's future; if that thread is interrupted while waiting, the library restores the interrupt flag and throws EngineInterruptedException (\"<interrupted>\"). This is the engine's typed wrapper so await-style callers unwind uniformly on cancellation.","triggerScenarios":"Awaiting (join) a promise whose future never completes while the host thread gets Thread.interrupt() — e.g. Karate step timeout, executor shutdown, or explicit cancellation.","commonSituations":"Test-suite shutdown while awaiting a never-resolving promise; watchdog timers interrupting the worker thread; await on a promise whose JS producer deadlocked or was cancelled (related to generator HOST_CANCELLED).","solutions":["Catch EngineInterruptedException and propagate/abort — do not swallow; the interrupt was intentional.","Ensure awaited promises always settle (resolve/reject) so joins don't linger until interruption.","Check for deadlock in the promise producer (unresolved inner promise, missing resolve callback).","If timeouts are expected, pass an explicit timeout instead of relying on interruption."],"exampleFix":"// before\nvar v = await neverSettling(); // hangs until thread interrupted\n// after\nvar v = await Promise.race([p, Promise.timeout(5000)]); // settles on its own","handlingStrategy":"try-catch","validationCode":"// before awaiting, ensure the producer can settle:\nif (p == null || typeof p.then !== 'function') throw new TypeError('not a promise');","typeGuard":"boolean isAwaitable(Object v) { return v != null && tryResolve(v) != null; } // Thenable check per spec","tryCatchPattern":"try { return p.join(timeout); } catch (EngineInterruptedException e) { Thread.currentThread().interrupt(); return null; /* or rethrow after cleanup */ }","preventionTips":["Always give awaited promises a bounded timeout so they settle on their own.","Guarantee resolve/reject on every path of promise-producing code.","Preserve the interrupt status; never swallow InterruptedException.","Watch for never-settling promises during shutdown — cancel them explicitly."],"tags":["promise","interrupted","concurrency"],"backgroundTag":"thread-interrupted","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"}