apache/druid · error · IllegalStateException
Can't start.
Error message
Can't start.
What it means
Thrown by CoordinatorBasicAuthenticatorCacheNotifier.start when the lifecycle lock reports the component cannot start. Druid lifecycle components must start exactly once from an idle state; if canStart() returns false the component is already started, starting, or has stopped/failed. The guard converts that illegal lifecycle state into an IllegalStateException.
Source
Thrown at extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authentication/db/cache/CoordinatorBasicAuthenticatorCacheNotifier.java:68
AuthenticatorMapper authenticatorMapper,
DruidNodeDiscoveryProvider discoveryProvider,
@EscalatedClient HttpClient httpClient
)
{
userCacheNotifier = new CommonCacheNotifier(
initAuthenticatorConfigMap(authenticatorMapper),
discoveryProvider,
httpClient,
"/druid-ext/basic-security/authentication/listen/%s",
"CoordinatorBasicAuthenticatorCacheNotifier"
);
}
@LifecycleStart
public void start()
{
if (!lifecycleLock.canStart()) {
throw new ISE("Can't start.");
}
try {
userCacheNotifier.start();
lifecycleLock.started();
}
finally {
lifecycleLock.exitStart();
}
}
@LifecycleStop
public void stop()
{
if (!lifecycleLock.canStop()) {
return;
}
try {View on GitHub (pinned to 9b90983fd2)
Solutions
- Ensure start() is invoked only once per instance and after construction/dependency injection completes
- Check lifecycleLock state; if already started, skip the call instead of throwing
- Restart the whole lifecycle (or create a new instance) instead of calling start on a stopped component
- Use Druid's Lifecycle/LeadershipLatch integration so the framework drives start/stop
Example fix
// before
if (!lifecycleLock.canStart()) { throw new ISE("Can't start."); }
// after
if (lifecycleLock.canStart()) { try { userCacheNotifier.start(); lifecycleLock.started(); } catch (Exception e) { lifecycleLock.stop(); throw e; } } // idempotent guard in caller Defensive patterns
Strategy: type-guard
Validate before calling
// guard in caller
boolean startIfIdle(CoordinatorBasicAuthenticatorCacheNotifier n) {
try { n.start(); return true; } catch (ISE e) { return false; } // already started/stopped
} Type guard
boolean isStartable(LifecycleLock lock) { return lock.canStart(); } Try / catch
try { notifier.start(); } catch (ISE e) { LOG.info("Cache notifier already started or stopped: %s", e.getMessage()); } Prevention
- Call start() exactly once per instance, driven by the Druid Lifecycle
- Never restart stopped components; construct a new instance instead
- Register lifecycle handlers so the framework manages start ordering
- Avoid concurrent manual start calls in tests
When it happens
Trigger: Calling start() twice on the same instance; calling start() after stop() or after a failed prior start; lifecycle races where two threads invoke start concurrently without the lock granting.
Common situations: Manual wiring of the notifier in tests calling start twice; lifecycle ordering mistakes where the component is restarted without being reset; concurrent lifecycle management from multiple threads.
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/b7a9fe7b38f90b88.
Report an issue: GitHub.