karatelabs/karate · error · EngineInterruptedException
setTimeout:
Error message
setTimeout:
What it means
The TimerScheduler threw a RuntimeException (typically RejectedExecutionException) when scheduling the timer's callback, meaning the scheduler's executor was stopped concurrently. setTimeout unwinds the timer record and its AsyncToken and wraps the cause in EngineInterruptedException so nothing leaks.
Solutions
- Check the wrapped cause (e.g. RejectedExecutionException) to confirm the executor was stopped, and stop scheduling at that point in your code
- Catch EngineInterruptedException and treat it as a benign shutdown signal
- Move timer scheduling earlier in the scenario lifecycle
- Ensure only the framework shuts the executor down, and never schedule from post-shutdown hooks
Example fix
// before
setTimeout(() => run(), 10); // RuntimeException: scheduler stopped
// after
try {
setTimeout(() => run(), 10);
} catch (e) {
if (!String(e).includes('RejectedExecutionException')) throw e;
} Defensive patterns
Strategy: try-catch
Try / catch
try {
setTimeout(fn, delay);
} catch (e) {
if (String(e).includes('RejectedExecutionException')) { /* scheduler stopped */ }
else throw e;
} Prevention
- Schedule timers only while the runner's executor is alive
- Centralize timer creation in framework-owned code
- Unwind timer records promptly on shutdown
When it happens
Trigger: setTimeout's timer.schedule() call lands after the scheduler's underlying executor has been shut down; any RuntimeException from the scheduling call is rethrown wrapped as 'setTimeout: <cause>'.
Common situations: RejectedExecutionException during engine teardown; executor shutdown racing timer creation from another thread; calling setTimeout from a hook that runs after the runner's executor is closed.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- setTimeout: karate is shutting down
- engine is poisoned:
- async function called outside an engine evaluation
- setTimeout: no engine is executing on this thread
- setTimeout requires an active evaluation
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/1a20e3c1289a6c93.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/js/AsyncSupport.java:782
retireTimer(record);
throw new EngineInterruptedException("setTimeout: karate is shutting down");
}
ScheduledFuture<?> future;
try {
future = timer.schedule(() -> {
// foreign thread: may only win the CAS and publish, never run JS
if (record.fire()) {
forgetTimer(record);
scope.removeTimer(id);
scope.publishSuccessor(record.token, AsyncJob.one(new AsyncJob.Timer(id, target, extra)));
}
}, delay);
} catch (RuntimeException e) {
// the scheduler was stopped concurrently (RejectedExecutionException):
// unwind the record and its token, or nothing ever releases them
scope.removeTimer(id);
retireTimer(record);
throw new EngineInterruptedException("setTimeout: " + e);
}
record.future = future;
if (!record.isLive()) {
// retired between the arm and the assignment — the future is ours to drop
future.cancel(false);
}
return id;
}
static Object clearTimeout(Context context, Object[] args) {
Engine engine = context == null ? null : context.getEngine();
AsyncScope scope = engine == null ? null : engine.currentScope();
if (scope == null) {
return Terms.UNDEFINED;
}
Object id = arg(args, 0);
Number n = id instanceof Number number ? number : Terms.objectToNumber(id);
if (n == null || Double.isNaN(n.doubleValue())) {View on GitHub (pinned to a22eb90246)