flowable/flowable-engine · warning
Timeout during shutdown of async job executor. The current r
Error message
Timeout during shutdown of async job executor. The current running jobs could not end within {} seconds after shutdown operation. What it means
This is a warning emitted by DefaultAsyncTaskExecutor.shutdown when executorService.awaitTermination does not complete within the configured await-termination period. It means some jobs were still running when the async job executor shut down; the JVM then proceeds to shut the executor down (executorService.shutdownNow follows), potentially interrupting those jobs mid-execution. Data consistency is guarded by the job retry/transaction mechanisms, but job progress may be lost.
Source
Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/async/DefaultAsyncTaskExecutor.java:116
public void start() {
if (executorService == null) {
this.executorService = initializeExecutor();
this.executorNeedsShutdown = true;
}
}
@Override
public void shutdown() {
if (executorService != null && executorNeedsShutdown) {
// Ask the thread pool to finish and exit
executorService.shutdown();
// Waits for the configured time to finish all currently executing jobs
try {
long secondsToWaitOnShutdown = configuration.getAwaitTerminationPeriod().getSeconds();
if (!executorService.awaitTermination(secondsToWaitOnShutdown, TimeUnit.SECONDS)) {
logger.warn(
"Timeout during shutdown of async job executor. The current running jobs could not end within {} seconds after shutdown operation.",
secondsToWaitOnShutdown);
}
} catch (InterruptedException e) {
logger.warn("Interrupted while shutting down the async job executor. ", e);
Thread.currentThread().interrupt();
}
executorService = null;
}
}
protected ExecutorService initializeExecutor() {
if (threadPoolQueue == null) {
int queueSize = getQueueSize();
logger.info("Creating thread pool queue of size {}", queueSize);
threadPoolQueue = new ArrayBlockingQueue<>(queueSize);
}View on GitHub (pinned to d6d39ce1c6)
Solutions
- Increase the await-termination period so running jobs can finish, e.g. flowable.async.executor.await-termination-period=60s (Spring) or configuration.setAwaitTerminationPeriod(Duration.ofSeconds(60))
- Reduce job duration/size: break long work into smaller async steps or set timeouts on external calls inside the job
- Check for stuck/hung jobs (e.g. blocked HTTP clients) and add timeouts or circuit breakers so workers free up at shutdown
- If graceful drain is not needed, treat this warning as expected; the engine will retry unfinished jobs on next startup
- Ensure jobs are actually processed during shutdown by stopping job acquisition first (flowable.async.executor.async-job-acquisition-enabled=false via actuator/pre-shutdown hook) if appropriate
Example fix
// before engineConfig.getAsyncExecutor().setAwaitTerminationPeriod(Duration.ofSeconds(2)); // after engineConfig.getAsyncExecutor().setAwaitTerminationPeriod(Duration.ofSeconds(120));
Defensive patterns
Strategy: validation
Validate before calling
// Before shutdown, check for jobs still in flight and size the termination window accordingly
long active = managementService.createJobQuery().count();
Duration wait = engineConfig.getAsyncExecutor().getAwaitTerminationPeriod();
if (active > 0 && wait.toSeconds() < expectedMaxJobSeconds) {
throw new IllegalStateException(active + " jobs running; await-termination-period " + wait + "s is too short");
} Try / catch
// No exception thrown; detect via elapsed shutdown time / job state after restart
try {
engine.close();
} catch (RuntimeException e) {
logger.warn("Async executor may have discarded running jobs; verify job retries on next startup", e);
} Prevention
- Set await-termination-period generously (>= worst-case job duration)
- Give every job a bounded duration: timeouts on HTTP calls inside service tasks
- Monitor managementService.createJobQuery().count() before deployments
- Drain jobs before planned shutdowns (stop acquisition, wait for queue to empty)
When it happens
Trigger: Calling shutdown (during engine close or application shutdown) while long-running async jobs are still executing and the configured awaitTerminationPeriod (flowable.async.executor.await-termination-period / defaultAsyncTaskExecutor) is shorter than the jobs' remaining runtime.
Common situations: Spring Boot app shut down or redeployed while long async jobs (timers, big service tasks, HTTP calls) run; await-termination period left at a small default; a job hung on a slow external service blocking the worker thread.
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.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Could not find activity ${activity} for processId ${processI
- Unable to close the local ProcessEngine
- Could not find a job with id '${jobId}'.
- max wait timeout has to be positive. It was
- None of the available futures completed within the max timeo
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/ece14dcaf4e5bf3f.
Report an issue: GitHub.