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
- Ensure start() succeeds before calling unregisterListener()
- Call unregisterListener()/stop only once per lifecycle
- 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
- Pair start()/unregisterListener() in try/finally
- Let Druid Lifecycle manage start/stop ordering
- Never unregister after a failed start; rebuild the instance
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.