karatelabs/karate · error · EngineInterruptedException
setTimeout: karate is shutting down
Error message
setTimeout: karate is shutting down
What it means
karate-js's setTimeout refuses to schedule a new async timer once the Karate lifecycle has left the RUNNING phase. Throwing EngineInterruptedException here prevents an unbacked AsyncToken from being created that could never be drained during shutdown. It is a deliberate fail-fast guard, not a bug.
Solutions
- Move the setTimeout call into running scenario/feature code so the lifecycle is in RUNNING phase
- Guard the call: check KarateLifecycle.phase() == RUNNING before scheduling
- Replace fire-and-forget timers with synchronous logic or Karate's built-in async/delay mechanisms
- If shutdown is unexpected, check for earlier failures (cancellation, thread interruption) that moved the lifecycle out of RUNNING
Example fix
// before
setTimeout(() => doWork(), 100); // may throw during teardown
// after
if (KarateLifecycle.phase() === 'RUNNING') {
setTimeout(() => doWork(), 100);
} Defensive patterns
Strategy: validation
Validate before calling
if (KarateLifecycle.phase() !== KarateLifecycle.Phase.RUNNING) {
// skip or defer the timer
return;
}
setTimeout(fn, delay); Prevention
- Only schedule timers from within running scenario code
- Avoid timers in afterScenario/afterFeature hooks
- Check lifecycle phase before async scheduling
When it happens
Trigger: Calling setTimeout (e.g. from JS `setTimeout(fn, delay)` or Java host code via hostAsyncCall) while KarateLifecycle.phase() is not RUNNING — i.e. before a run starts, during teardown, or after a suite has been aborted/cancelled.
Common situations: Background timers kicked off from after-scenario hooks or cleanup code; a JS callback that schedules a follow-up timer while the engine is being torn down; test-parallel worker shutdown racing a pending setTimeout call.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- setTimeout:
- karate.driver can only be read within a scenario
- channel() can only be called within a scenario
- karate.setup() is not available in this context
- engine is poisoned:
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/11d3dff9b095ec45.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/js/AsyncSupport.java:749
return engine;
}
static Object setTimeout(Context context, Object[] args) {
Engine engine = timerEngine(context);
AsyncScope scope = engine.currentScope();
if (scope == null) {
throw JsErrorException.typeError("setTimeout requires an active evaluation");
}
Object callback = arg(args, 0);
if (!(callback instanceof JsCallable target)) {
throw JsErrorException.typeError("setTimeout: callback is not a function");
}
long delay = coerceDelay(arg(args, 1));
Object[] extra = args.length > 2 ? Arrays.copyOfRange(args, 2, args.length) : EMPTY_ARGS;
if (KarateLifecycle.phase() != KarateLifecycle.Phase.RUNNING) {
// refuse before a token is acquired — scheduling is about to be
// rejected anyway, and an unbacked token would strand the drain
throw new EngineInterruptedException("setTimeout: karate is shutting down");
}
AsyncToken token = scope.acquire("timer");
if (token == null) {
throw new EngineInterruptedException();
}
AsyncActivation.anyAsync = true;
int id = scope.nextTimerId();
AsyncScope.TimerRecord record = new AsyncScope.TimerRecord(scope, id, token);
scope.addTimer(record);
TimerScheduler timer = scheduler();
// the closed-check and the registration are one atomic step under the
// scheduler's gate — see TimerScheduler for why ordering alone would not do
if (!timer.register(record)) {
scope.removeTimer(id);
retireTimer(record);
throw new EngineInterruptedException("setTimeout: karate is shutting down");
}
ScheduledFuture<?> future;View on GitHub (pinned to a22eb90246)