apache/druid · error · IllegalStateException

can't stop.

Error message

can't stop.

What it means

CuratorDruidNodeDiscoveryProvider.stop() is guarded by LifecycleStop and throws ISE("can't stop.") when the lifecycle lock disallows stopping — typically because the provider was never started or is already stopping/stopped.

Source

Thrown at server/src/main/java/org/apache/druid/curator/discovery/CuratorDruidNodeDiscoveryProvider.java:150

    try {
      // This is single-threaded to ensure that all listener calls are executed precisely in the order of add/remove
      // event occurrences.
      listenerExecutor = Execs.scheduledSingleThreaded("CuratorDruidNodeDiscoveryProvider-ListenerExecutor");

      log.debug("Started.");

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

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

    log.debug("Stopping.");

    Closer closer = Closer.create();
    closer.registerAll(nodeRoleWatchers.values());
    closer.registerAll(nodeDiscoverers);

    CloseableUtils.closeAll(closer, listenerExecutor::shutdownNow);
  }

  private static class NodeRoleWatcher implements DruidNodeDiscovery, Closeable
  {
    private static final Logger log = new Logger(NodeRoleWatcher.class);

    private final CuratorFramework curatorFramework;

    private final NodeRole nodeRole;

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Only call stop() after a successful start()
  2. Make stop idempotent at the call site (track a stopped flag or route through the Lifecycle)
  3. In tests, start the provider in setup before stopping in teardown

Example fix

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

Strategy: type-guard

Validate before calling

if (!lifecycleLock.canStop()) {
  return; // never started or already stopping
}

Type guard

boolean startedAndStoppable = lifecycleLock.canStop();

Try / catch

try {
  provider.stop();
} catch (IllegalStateException e) {
  if ("can't stop.".equals(e.getMessage())) {
    log.debug("provider was not started; nothing to stop");
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling stop() before start(), calling stop() twice, or concurrent stop during shutdown; observed via the test_stop_interruptsPollingThread path.

Common situations: Shutdown hooks firing after an earlier stop, tests tearing down fixtures that never started, Lifecycle stop racing with an error path that already stopped it.

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/f646ee8ba34b8ef9. Report an issue: GitHub.