karatelabs/karate · error · io.karatelabs.js.EngineTimeoutException

timed out waiting for promise

Error message

timed out waiting for promise

What it means

join(timeout) waits on the promise future with a deadline; when it elapses, TimeoutException is translated into EngineTimeoutException("timed out waiting for promise"). It means the awaited promise did not settle within the allowed time — the JS producer never resolved or rejected.

Solutions

  1. Increase the timeout passed to join/await if the operation is legitimately slow.
  2. Audit the promise producer: ensure resolve/reject is always invoked on every code path.
  3. Add an upstream timeout to the slow operation (HTTP client timeout) so the promise rejects instead of hanging forever.
  4. Catch EngineTimeoutException and apply retry/fallback logic for transient slowness.

Example fix

// before
new Promise((resolve) => { /* resolve never called */ })
// after
new Promise((resolve, reject) => { setTimeout(() => resolve(val), 100); })
Defensive patterns

Strategy: retry

Validate before calling

// pre-check the awaited operation's own timeout is smaller than the join timeout:
if (timeout != null && upstreamTimeout > timeout.toMillis()) warn("join timeout shorter than upstream timeout");

Try / catch

try { return p.join(timeout); } catch (EngineTimeoutException e) { log.warn("promise did not settle within " + timeout); return fallback(); }

Prevention

When it happens

Trigger: await/join with a non-null timeout on a promise that stays pending: unresolved executor callback, a network call with no response, a generator that never resumes, or a timeout set smaller than the work required.

Common situations: Waiting on a fetch/backend call that hangs; forgetting to call resolve() in a Promise constructor; overly tight timeout configured in Karate steps wrapping async JS; deadlocked generator chain.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/JsPromise.java:242

     * this from the engine thread would wait for a queue that nothing is
     * pumping.
     */
    public Object await() {
        return join(null);
    }

    /** {@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;

View on GitHub (pinned to a22eb90246)