{"record":{"id":"7ab6a6940b4c62ed","repo":"junit-team/junit5","slug":"scheduled-executor-could-not-be-stopped-in-an-orde","errorCode":null,"errorMessage":"Scheduled executor could not be stopped in an orderly manner","messagePattern":"Scheduled executor could not be stopped in an orderly manner","errorType":"exception","errorClass":"JUnitException","httpStatus":null,"severity":"error","filePath":"junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutInvocationFactory.java","lineNumber":71,"sourceCode":"\tabstract static class ExecutorResource implements Store.CloseableResource, AutoCloseable {\n\n\t\tprivate final ScheduledExecutorService executor;\n\n\t\tExecutorResource(ScheduledExecutorService executor) {\n\t\t\tthis.executor = executor;\n\t\t}\n\n\t\tScheduledExecutorService get() {\n\t\t\treturn executor;\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() throws Exception {\n\t\t\texecutor.shutdown();\n\t\t\tboolean terminated = executor.awaitTermination(5, TimeUnit.SECONDS);\n\t\t\tif (!terminated) {\n\t\t\t\texecutor.shutdownNow();\n\t\t\t\tthrow new JUnitException(\"Scheduled executor could not be stopped in an orderly manner\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"try\")\n\tstatic class SingleThreadExecutorResource extends ExecutorResource {\n\n\t\t@SuppressWarnings({ \"unused\", \"ThreadPriorityCheck\" })\n\t\tSingleThreadExecutorResource() {\n\t\t\tsuper(Executors.newSingleThreadScheduledExecutor(runnable -> {\n\t\t\t\tThread thread = new Thread(runnable, \"junit-jupiter-timeout-watcher\");\n\t\t\t\tthread.setPriority(Thread.MAX_PRIORITY);\n\t\t\t\treturn thread;\n\t\t\t}));\n\t\t}\n\t}\n\n}","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/junit-team/junit5/blob/f070c699a08b5d8393df9afd147af5c5e90bb21b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutInvocationFactory.java#L53-L89","documentation":"Thrown when the scheduled executor service used for timeout monitoring fails to terminate within 5 seconds of calling shutdown(), even after shutdownNow() is invoked. This indicates the timeout watcher thread is stuck — possibly blocked on an I/O operation, in an infinite loop, or holding a lock. The executor runs with Thread.MAX_PRIORITY under the name 'junit-jupiter-timeout-watcher'.","triggerScenarios":"The SAME_THREAD timeout invocation's ScheduledExecutorService is closed (typically at the end of the test context lifecycle) and its awaitTermination(5, SECONDS) returns false. This happens when the watcher thread is still executing or blocked. The underlying test code under timeout management is non-cooperative with thread interruption.","commonSituations":"Tests that spawn their own threads or perform blocking I/O (network calls, file locks, database waits) that don't respond to interruption. Long-running tearDown or @AfterEach methods that exceed the 5-second grace window. Tests running in environments with heavy GC pressure or thread starvation where the watcher thread doesn't get scheduled in time.","solutions":["Ensure test code and lifecycle methods respond to Thread.interrupt() by checking Thread.currentThread().isInterrupted() or catching InterruptedException","Close any network connections, file handles, or database resources in try-finally blocks so they don't block the watcher thread","Increase timeout margins so the watched code completes well before the executor shutdown","If the issue is environmental (CI under load), reduce parallel test execution or increase available CPU"],"exampleFix":"// before — blocking call that ignores interruption\nwhile (!dataReady()) {\n    Thread.sleep(100);\n}\n\n// after — cooperative with interruption\nwhile (!dataReady()) {\n    if (Thread.currentThread().isInterrupted()) {\n        throw new RuntimeException(\"Interrupted while waiting\");\n    }\n    Thread.sleep(100);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// If managing the executor lifecycle in a custom extension\ntry {\n    executor.shutdown();\n    if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {\n        executor.shutdownNow();\n        logger.warning(\"Timeout watcher executor did not terminate cleanly\");\n    }\n} catch (InterruptedException e) {\n    Thread.currentThread().interrupt();\n    executor.shutdownNow();\n}","preventionTips":["Ensure test code and lifecycle methods are responsive to Thread.interrupt() — check isInterrupted() in loops","Close all I/O resources (sockets, files, DB connections) in try-finally blocks in test methods and @AfterEach","Avoid infinite loops or unbounded blocking operations in tests that run under @Timeout","Monitor for thread leaks in CI to detect stuck watcher threads early"],"tags":["timeout","threading","executor","resource-leak","junit-jupiter"],"backgroundTag":null,"analyzedSha":"f070c699a08b5d8393df9afd147af5c5e90bb21b","analyzedAt":"2026-08-11T20:31:00.530Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}