apache/druid · error · IllegalStateException
can't start.
Error message
can't start.
What it means
LeaderElectorAsyncWrapper.run() throws this IllegalStateException when its LifecycleLock disallows start - i.e. the elector is already running, stopping, or was previously stopped. The wrapper submits the leader-election loop to a single-threaded executor exactly once; a second run() would create competing leadership callbacks.
Source
Thrown at extensions-core/kubernetes-extensions/src/main/java/org/apache/druid/k8s/discovery/LeaderElectorAsyncWrapper.java:72
K8sDiscoveryConfig.K8S_RESOURCE_NAME_REGEX.matcher(lockResourceName).matches(),
"lockResourceName[%s] must match regex[%s]",
lockResourceName,
K8sDiscoveryConfig.K8S_RESOURCE_NAME_REGEX.pattern()
);
LOGGER.info(
"Creating LeaderElector with candidateId[%s], lockResourceName[%s], k8sNamespace[%s].",
candidateId,
lockResourceName,
lockResourceNamespace
);
k8sLeaderElector = k8sLeaderElectorFactory.create(candidateId, lockResourceNamespace, lockResourceName);
}
public void run(Runnable startLeadingHook, Runnable stopLeadingHook)
{
if (!lifecycleLock.canStart()) {
throw new ISE("can't start.");
}
try {
executor = Execs.singleThreaded(this.getClass().getSimpleName());
futureRef.set(executor.submit(
() -> {
while (lifecycleLock.awaitStarted(1, TimeUnit.MILLISECONDS)) {
try {
k8sLeaderElector.run(startLeadingHook, stopLeadingHook);
}
catch (Throwable ex) {
LOGGER.error(ex, "Exception in K8s LeaderElector.run()");
}
}
}
));
lifecycleLock.started();
}View on GitHub (pinned to 9b90983fd2)
Solutions
- Call run() once per LeaderElectorAsyncWrapper instance; create a new wrapper for re-election
- Close the old elector and construct a fresh one before calling run() again
- Synchronize callers (startLeaderElector) so run() cannot race with itself
- Verify via logs - repeated 'LeaderElectorAsyncWrapper' thread creation means run() was called twice
Example fix
// before elector.run(leadHook, stopHook); // re-election elector.run(leadHook, stopHook); // ISE // after elector.close(); elector = new LeaderElectorAsyncWrapper(...); elector.run(leadHook, stopHook);
Defensive patterns
Strategy: validation
Validate before calling
if (electorRunning) { return; } Try / catch
try { elector.run(startLeadingHook, stopLeadingHook); } catch (IllegalStateException e) { log.warn("elector already running or closed"); } Prevention
- One run() per wrapper instance
- Close and rebuild the wrapper for re-election
- Guard startLeaderElector against concurrent invocation
When it happens
Trigger: Calling run() twice on the same wrapper (e.g. re-election attempt after losing leadership); concurrent run() invocations; calling run() after close(); startLeaderElector invoked again by task-runner reconfiguration.
Common situations: Overlord candidate re-joining an election after a network blip while the old elector still exists; double injection of the leader-wrangler in Druid task-runner master election; unit tests reusing one wrapper across cases.
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/3374af192ec7b869.
Report an issue: GitHub.