karatelabs/karate · warning
timed out stopping after ms
Error message
timed out stopping {} after {}ms What it means
stopBounded() submits the Stoppable's stop logic to a thread pool and waits future.get(budgetNanos). When the stoppable does not finish within the budget, a TimeoutException is caught, the future is cancelled (interrupting the stop thread), and this warning is logged with Outcome.TIMED_OUT. The resource may still be in the process of shutting down or may be left partially stopped.
Solutions
- Increase the shutdown budget/timeout so the stoppable can finish gracefully
- Find the resource named in the warning and fix its stop() path (close connections, drain queues) so it stops quickly
- Reduce open work (in-flight requests, long polling) before shutdown begins
- If the resource is known-safe to abandon, suppress or ignore the warning and ensure the JVM exits (e.g. System.exit or daemon threads)
Example fix
// before KarateLifecycle.budgetMillis = 500; // server.stop() takes ~2s // after KarateLifecycle.budgetMillis = 5000; // allow graceful stop
Defensive patterns
Strategy: try-catch
Try / catch
try { lifecycle.stop(); } catch (Exception e) { logger.warn('shutdown incomplete: {}', e.getMessage()); /* force cleanup */ }
Prevention
- Size the shutdown budget to the slowest stoppable
- Fix stop() implementations to honor interrupts
- Close connections/queues before shutdown
- Take thread dumps when timeouts recur
When it happens
Trigger: Any Stoppable whose stop() implementation takes longer than the remaining shutdown budget: a stuck HTTP server waiting on open connections, a mock with long in-flight requests, a background process ignoring interrupts, or a stop() that blocks on I/O or locks.
Common situations: Embedded servers holding keep-alive connections; CI pipelines with tight shutdown timeouts; background JVM processes that don't respond to Thread.interrupt(); resources registered but never given a chance to drain.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- no time left to stop
- setTimeout: karate is shutting down
- ext onShutdown failed
- registered while , stopping immediately
- shutdown in progress failed
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/7c8bd0282c5647d9.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/common/KarateLifecycle.java:376
return new StopResult(name, kind, Outcome.TIMED_OUT, Duration.ZERO);
}
logger.debug("stopping {} [{}]", name, kind);
Future<?> future = pool().submit(() -> {
SHUTDOWN_THREAD.set(true);
try {
stoppable.stop();
} finally {
SHUTDOWN_THREAD.remove();
}
});
Outcome outcome;
try {
future.get(budgetNanos, TimeUnit.NANOSECONDS);
outcome = Outcome.STOPPED;
} catch (TimeoutException e) {
future.cancel(true);
outcome = Outcome.TIMED_OUT;
logger.warn("timed out stopping {} after {}ms", name, TimeUnit.NANOSECONDS.toMillis(budgetNanos));
} catch (ExecutionException e) {
Throwable cause = e.getCause() == null ? e : e.getCause();
outcome = Outcome.FAILED;
logger.warn("failed to stop {}: {}", name, cause.getMessage());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
future.cancel(true);
outcome = Outcome.TIMED_OUT;
logger.warn("interrupted while stopping {}", name);
}
return new StopResult(name, kind, outcome, Duration.ofNanos(System.nanoTime() - start));
}
private static ExecutorService pool() {
synchronized (LOCK) {
if (stopPool == null) {
// cached, so a worker stuck on a stop() that ignores interrupts never blocks the next
stopPool = Executors.newCachedThreadPool(ThreadUtils.daemonFactory("karate-lifecycle-"));View on GitHub (pinned to a22eb90246)