apache/druid · error · IllegalStateException

can't stop.

Error message

can't stop.

What it means

CuratorDruidLeaderSelector.unregisterListener is guarded by the LifecycleLock; if canStop() returns false (never started, already stopped, or concurrently stopping), it throws ISE("can't stop."). It enforces start-before-stop lifecycle ordering.

Source

Thrown at server/src/main/java/org/apache/druid/curator/discovery/CuratorDruidLeaderSelector.java:205

      createNewLeaderLatchWithListener();
      leaderLatch.get().start();

      lifecycleLock.started();
    }
    catch (Exception ex) {
      throw new RuntimeException(ex);
    }
    finally {
      lifecycleLock.exitStart();
    }
  }

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

    CloseableUtils.closeAndSuppressExceptions(leaderLatch.get(), e -> log.warn(e, "Failed to close LeaderLatch."));
    listenerExecutor.shutdownNow();
  }

  private void stopAndCreateNewLeaderLatch()
  {
    CloseableUtils.closeAndSuppressExceptions(
        createNewLeaderLatchWithListener(),
        e -> log.warn("Could not close old leader latch; continuing with new one anyway.")
    );
  }

  private void startLeaderLatch()
  {
    try {
      //Small delay before starting the latch so that others waiting are chosen to become leader.

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Ensure start() succeeds before calling unregisterListener()
  2. Call unregisterListener()/stop only once per lifecycle
  3. Guard callers with lifecycleLock.awaitStarted() or check state before stopping

Example fix

// before
leaderSelector.unregisterListener();
// after
if (lifecycleLock.awaitStarted(1, TimeUnit.SECONDS)) {
  leaderSelector.unregisterListener();
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!lifecycleLock.canStop()) {
  log.warn("LeaderSelector not started; skipping unregister");
  return;
}

Type guard

boolean safeToUnregister = lifecycleLock.canStop();

Try / catch

try {
  leaderSelector.unregisterListener();
} catch (IllegalStateException e) {
  if ("can't stop.".equals(e.getMessage())) {
    log.debug("selector never started; ignoring unregister");
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling unregisterListener() before start() completed, calling it twice, or after start() failed so the lock never reached the started state.

Common situations: Shutdown hooks firing on partially-initialized services, tests stopping components without starting them, exception during start leaving the lock in a bad state.

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