{"record":{"id":"ac41309e0c2e3b5b","repo":"karatelabs/karate","slug":"executor-did-not-terminate","errorCode":null,"errorMessage":"executor did not terminate: {}","messagePattern":"executor did not terminate: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"karate-js/src/main/java/io/karatelabs/common/KarateLifecycle.java","lineNumber":507,"sourceCode":"        @Override\n        public String lifecycleKind() {\n            return kind;\n        }\n\n        @Override\n        public ExecutorService lifecycleExecutor() {\n            return executor;\n        }\n\n        @Override\n        public void stop() {\n            if (executor == null || executor.isTerminated()) {\n                return;\n            }\n            executor.shutdownNow();\n            try {\n                if (!executor.awaitTermination(DRAIN_MILLIS, TimeUnit.MILLISECONDS)) {\n                    logger.warn(\"executor did not terminate: {}\", name);\n                }\n            } catch (InterruptedException e) {\n                Thread.currentThread().interrupt();\n            }\n        }\n\n        @Override\n        public String toString() {\n            return name;\n        }\n\n    }\n\n}\n","sourceCodeStart":489,"sourceCodeEnd":522,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/common/KarateLifecycle.java#L489-L522","documentation":"KarateLifecycle.stop() shuts down its internal executor and calls awaitTermination(DRAIN_MILLIS). If the executor's threads still have not terminated after the drain window, shutdownNow() has already interrupted them and this warning logs that termination did not complete. Non-daemon worker threads may keep the JVM alive or tasks may be abandoned mid-run.","triggerScenarios":"Executor tasks that ignore interruption or block on non-interruptible I/O/locks during shutdown; very short DRAIN_MILLIS relative to work in flight; a submitted stoppable task hung inside stop().","commonSituations":"Hung background servers during suite teardown; CI agents where the process lingers after tests; leaked threads from user code registered as Stoppables.","solutions":["Ensure all registered Stoppables stop promptly (fix their stop() implementations)","Investigate which task blocks the executor — take a thread dump at shutdown to identify the stuck thread","Allow more drain time if work legitimately needs it","Use daemon threads for pooled work so a non-terminated executor cannot keep the JVM alive"],"exampleFix":"// before: task blocks on socket read, ignores interrupt\nsocket.getInputStream().read();\n// after\nsocket.setSoTimeout(1000); // read becomes interruptible, stop completes","handlingStrategy":"validation","validationCode":"// verify executor health before shutdown\nif (!executor.isTerminated()) { executor.shutdownNow(); }\n","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Make pooled tasks interruptible (socket timeouts, interruptible locks)","Use daemon threads for background work","Avoid blocking I/O without timeouts in Stoppables","Audit leaked threads with a shutdown thread dump"],"tags":["lifecycle","executor","shutdown","thread-pool"],"backgroundTag":"shutdown-timeout","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}