{"record":{"id":"eed1fbd74fcf8c61","repo":"apache/druid","slug":"can-t-stop-eed1fb","errorCode":null,"errorMessage":"can't stop.","messagePattern":"can't stop\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions-core/kubernetes-extensions/src/main/java/org/apache/druid/k8s/discovery/K8sDruidNodeDiscoveryProvider.java","lineNumber":163,"sourceCode":"\n      // This is single-threaded to ensure that all listener calls are executed precisely in the oder of add/remove\n      // event occurences.\n      listenerExecutor = Execs.scheduledSingleThreaded(\"K8sDruidNodeDiscoveryProvider-ListenerExecutor\");\n\n      LOGGER.info(\"started\");\n\n      lifecycleLock.started();\n    }\n    finally {\n      lifecycleLock.exitStart();\n    }\n  }\n\n  @LifecycleStop\n  public void stop()\n  {\n    if (!lifecycleLock.canStop()) {\n      throw new ISE(\"can't stop.\");\n    }\n\n    LOGGER.info(\"stopping\");\n\n    for (NodeRoleWatcher watcher : nodeTypeWatchers.values()) {\n      watcher.stop();\n    }\n    listenerExecutor.shutdownNow();\n\n    LOGGER.info(\"stopped\");\n  }\n\n  @VisibleForTesting\n  static class NodeRoleWatcher implements DruidNodeDiscovery\n  {\n    private static final Logger LOGGER = new Logger(NodeRoleWatcher.class);\n\n    private final PodInfo podInfo;","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-core/kubernetes-extensions/src/main/java/org/apache/druid/k8s/discovery/K8sDruidNodeDiscoveryProvider.java#L145-L181","documentation":"K8sDruidNodeDiscoveryProvider.stop() throws this IllegalStateException when the LifecycleLock says the instance is not in a started state (or already stopping/stopped). Stopping an unstarted provider would attempt to shut down NodeRoleWatcher threads that were never created.","triggerScenarios":"Calling stop() before start(); calling stop() twice; calling stop() concurrently with a start that hasn't finished; calling stop() after start() threw before completing successfully.","commonSituations":"@AfterEach teardown in tests when @BeforeEach start failed; Druid shutdown hook firing after a failed startup; double-close through both Lifecycle and try-with-resources.","solutions":["Only call stop() after a confirmed successful start()","In tests, guard teardown: track whether start succeeded before stopping","Catch/ignore IllegalStateException in cleanup code if the start may have failed","Create a fresh instance rather than stopping an already-stopped provider"],"exampleFix":"// before\nprovider.stop(); // may throw if never started\n// after\ntry {\n  provider.stop();\n} catch (IllegalStateException e) {\n  // already stopped or never started\n}","handlingStrategy":"try-catch","validationCode":"if (!started) { return; } // nothing to stop","typeGuard":null,"tryCatchPattern":"try { provider.stop(); } catch (IllegalStateException e) { /* not started or already stopped */ }","preventionTips":["Only stop after a confirmed successful start","Track lifecycle state with a boolean before delegating","Avoid double-close via both Lifecycle registration and manual stop"],"tags":["lifecycle","illegal-state","kubernetes","double-stop"],"backgroundTag":"invalid-state-transition","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}