apache/druid · error · IllegalStateException

can't stop.

Error message

can't stop.

What it means

LeaderElectorAsyncWrapper.close() throws this IllegalStateException when LifecycleLock.canStop() returns false - the elector was never started via run(), is already closed, or is mid-close. The close path cancels the election future and shuts down the executor, which only exist after a successful run().

Source

Thrown at extensions-core/kubernetes-extensions/src/main/java/org/apache/druid/k8s/discovery/LeaderElectorAsyncWrapper.java:100

              }
              catch (Throwable ex) {
                LOGGER.error(ex, "Exception in K8s LeaderElector.run()");
              }
            }
          }
      ));
      lifecycleLock.started();
    }
    finally {
      lifecycleLock.exitStart();
    }
  }

  @Override
  public void close()
  {
    if (!lifecycleLock.canStop()) {
      throw new ISE("can't stop.");
    }

    try {
      futureRef.get().cancel(true);
      executor.shutdownNow();
      if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
        LOGGER.warn("Failed to terminate [%s] executor.", this.getClass().getSimpleName());
      }
    }
    catch (InterruptedException ex) {
      Thread.currentThread().interrupt();
    }
  }

  public String getCurrentLeader()
  {
    return k8sLeaderElector.getCurrentLeader();
  }

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Only close() after run() has been called successfully
  2. Guard close with try-catch of IllegalStateException if the start state is unknown
  3. Avoid double-close: don't both register with Lifecycle and close manually
  4. If close must be idempotent, check an internal 'started' flag before delegating

Example fix

// before
elector.close(); // ISE if run() never called
// after
if (started) {
  elector.close();
  started = false;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!electorRunning) { return; }

Try / catch

try { elector.close(); } catch (IllegalStateException e) { /* never started or already closed */ }

Prevention

When it happens

Trigger: Calling close() before run(); calling close() twice (double close / try-with-resources plus manual close); close() racing run(); run() previously failed leaving state stuck.

Common situations: Overlord shutdown where the elector failed to start; cleanup code closing all services unconditionally; wrapping an unstarted elector in try-with-resources.

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/73194911e0afd2f6. Report an issue: GitHub.