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

  1. Call run() once per LeaderElectorAsyncWrapper instance; create a new wrapper for re-election
  2. Close the old elector and construct a fresh one before calling run() again
  3. Synchronize callers (startLeaderElector) so run() cannot race with itself
  4. 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

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.