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
- Only call stop() after a successful start()
- Make stop idempotent at the call site (track a stopped flag or route through the Lifecycle)
- 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
- Track start success and only stop if started
- In tests, start in setup before stopping in teardown
- Avoid duplicate stop paths (Lifecycle + manual stop)
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.