apache/druid · error · IllegalStateException

can't stop.

Error message

can't stop.

What it means

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.

Source

Thrown at extensions-contrib/consul-extensions/src/main/java/org/apache/druid/consul/discovery/ConsulDruidNodeDiscoveryProvider.java:155

      LOGGER.info("Starting ConsulDruidNodeDiscoveryProvider");

      // Single-threaded executor ensures listener callbacks execute in-order, preventing race conditions
      listenerExecutor = Execs.scheduledSingleThreaded("ConsulDruidNodeDiscoveryProvider-ListenerExecutor");

      LOGGER.info("Started ConsulDruidNodeDiscoveryProvider");

      lifecycleLock.started();
    }
    finally {
      lifecycleLock.exitStart();
    }
  }

  @LifecycleStop
  public void stop()
  {
    if (!lifecycleLock.canStop()) {
      throw new ISE("can't stop.");
    }

    LOGGER.info("Stopping ConsulDruidNodeDiscoveryProvider");

    for (NodeRoleWatcher watcher : nodeRoleWatchers.values()) {
      watcher.stop();
    }
    nodeRoleWatchers.clear();

    // Watcher threads must finish before shutting down listener executor to avoid RejectedExecutionException
    try {
      listenerExecutor.shutdown();
      if (!listenerExecutor.awaitTermination(10, TimeUnit.SECONDS)) {
        LOGGER.warn("Listener executor did not terminate in time");
        listenerExecutor.shutdownNow();
      }
    }
    catch (InterruptedException e) {

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Ensure start() is invoked (and completes) before stop(); only call stop() after a successful start().
  2. Guard your shutdown code with lifecycleLock.awaitStopped()/state checks instead of calling stop() unconditionally.
  3. In tests, register the provider with a Lifecycle and stop the Lifecycle rather than calling stop() directly.
  4. Check for earlier exceptions during start() that prevented the lock from entering the started state.

Example fix

// before
provider.stop(); // throws if never started
// after
if (started) {
  provider.stop();
  started = false;
}
Defensive patterns

Strategy: validation

Validate before calling

if (!started || stopping) { return; }
provider.stop();

Prevention

When it happens

Trigger: Calling stop() before start() has ever succeeded, calling stop() twice, or racing stop() against a concurrent start() (canStop() returns false).

Common situations: 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.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/01c5c9658531b5d6. Report an issue: GitHub.