{"record":{"id":"ece14dcaf4e5bf3f","repo":"flowable/flowable-engine","slug":"timeout-during-shutdown-of-async-job-executor-the","errorCode":null,"errorMessage":"Timeout during shutdown of async job executor. The current running jobs could not end within {} seconds after shutdown operation.","messagePattern":"Timeout during shutdown of async job executor\\. The current running jobs could not end within (.+?) seconds after shutdown operation\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/async/DefaultAsyncTaskExecutor.java","lineNumber":116,"sourceCode":"\n    public void start() {\n        if (executorService == null) {\n            this.executorService = initializeExecutor();\n            this.executorNeedsShutdown = true;\n        }\n    }\n\n    @Override\n    public void shutdown() {\n        if (executorService != null && executorNeedsShutdown) {\n            // Ask the thread pool to finish and exit\n            executorService.shutdown();\n\n            // Waits for the configured time to finish all currently executing jobs\n            try {\n                long secondsToWaitOnShutdown = configuration.getAwaitTerminationPeriod().getSeconds();\n                if (!executorService.awaitTermination(secondsToWaitOnShutdown, TimeUnit.SECONDS)) {\n                    logger.warn(\n                            \"Timeout during shutdown of async job executor. The current running jobs could not end within {} seconds after shutdown operation.\",\n                            secondsToWaitOnShutdown);\n                }\n            } catch (InterruptedException e) {\n                logger.warn(\"Interrupted while shutting down the async job executor. \", e);\n                Thread.currentThread().interrupt();\n            }\n\n            executorService = null;\n        }\n    }\n\n    protected ExecutorService initializeExecutor() {\n        if (threadPoolQueue == null) {\n            int queueSize = getQueueSize();\n            logger.info(\"Creating thread pool queue of size {}\", queueSize);\n            threadPoolQueue = new ArrayBlockingQueue<>(queueSize);\n        }","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/async/DefaultAsyncTaskExecutor.java#L98-L134","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nengineConfig.getAsyncExecutor().setAwaitTerminationPeriod(Duration.ofSeconds(2));\n// after\nengineConfig.getAsyncExecutor().setAwaitTerminationPeriod(Duration.ofSeconds(120));","handlingStrategy":"validation","validationCode":"// Before shutdown, check for jobs still in flight and size the termination window accordingly\nlong active = managementService.createJobQuery().count();\nDuration wait = engineConfig.getAsyncExecutor().getAwaitTerminationPeriod();\nif (active > 0 && wait.toSeconds() < expectedMaxJobSeconds) {\n  throw new IllegalStateException(active + \" jobs running; await-termination-period \" + wait + \"s is too short\");\n}","typeGuard":null,"tryCatchPattern":"// No exception thrown; detect via elapsed shutdown time / job state after restart\ntry {\n  engine.close();\n} catch (RuntimeException e) {\n  logger.warn(\"Async executor may have discarded running jobs; verify job retries on next startup\", e);\n}","preventionTips":["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)"],"tags":["async-executor","shutdown","timeout","job-execution"],"backgroundTag":"request-timeout","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}