{"record":{"id":"01c5c9658531b5d6","repo":"apache/druid","slug":"can-t-stop-01c5c9","errorCode":null,"errorMessage":"can't stop.","messagePattern":"can't stop\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions-contrib/consul-extensions/src/main/java/org/apache/druid/consul/discovery/ConsulDruidNodeDiscoveryProvider.java","lineNumber":155,"sourceCode":"      LOGGER.info(\"Starting ConsulDruidNodeDiscoveryProvider\");\n\n      // Single-threaded executor ensures listener callbacks execute in-order, preventing race conditions\n      listenerExecutor = Execs.scheduledSingleThreaded(\"ConsulDruidNodeDiscoveryProvider-ListenerExecutor\");\n\n      LOGGER.info(\"Started ConsulDruidNodeDiscoveryProvider\");\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 ConsulDruidNodeDiscoveryProvider\");\n\n    for (NodeRoleWatcher watcher : nodeRoleWatchers.values()) {\n      watcher.stop();\n    }\n    nodeRoleWatchers.clear();\n\n    // Watcher threads must finish before shutting down listener executor to avoid RejectedExecutionException\n    try {\n      listenerExecutor.shutdown();\n      if (!listenerExecutor.awaitTermination(10, TimeUnit.SECONDS)) {\n        LOGGER.warn(\"Listener executor did not terminate in time\");\n        listenerExecutor.shutdownNow();\n      }\n    }\n    catch (InterruptedException e) {","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-contrib/consul-extensions/src/main/java/org/apache/druid/consul/discovery/ConsulDruidNodeDiscoveryProvider.java#L137-L173","documentation":"ConsulDruidNodeDiscoveryProvider.stop() throws this ISE when the lifecycle lock reports the component is not in a startable/stopped-appropriate state, i.e. stop() was called before start() or while a start is in progress. Druid components guard their lifecycle with LifecycleLock so stop/start happen exactly once in the right order.","triggerScenarios":"Calling stop() before start() has ever succeeded, calling stop() twice, or racing stop() against a concurrent start() (canStop() returns false).","commonSituations":"Manual lifecycle manipulation in tests or embedded setups, a start() failure that left the lock in started=false, or double shutdown from both a Lifecycle handler and application shutdown hooks.","solutions":["Ensure start() is invoked (and completes) before stop(); only call stop() after a successful start().","Guard your shutdown code with lifecycleLock.awaitStopped()/state checks instead of calling stop() unconditionally.","In tests, register the provider with a Lifecycle and stop the Lifecycle rather than calling stop() directly.","Check for earlier exceptions during start() that prevented the lock from entering the started state."],"exampleFix":"// before\nprovider.stop(); // throws if never started\n// after\nif (started) {\n  provider.stop();\n  started = false;\n}","handlingStrategy":"validation","validationCode":"if (!started || stopping) { return; }\nprovider.stop();","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Manage component lifecycle exclusively through a Druid Lifecycle instance.","Never call stop() more than once; guard with an AtomicBoolean.","Check for start() failures in logs before attempting shutdown."],"tags":["lifecycle","consul","illegal-state"],"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-14T05:17:10.506Z"}