apache/druid · error · IllegalStateException

Could not start lifecycle

Error message

Could not start lifecycle

What it means

HttpServerInventoryView.start() guards startup with a LifecycleLock. If canStart() returns false — the lifecycle is already started, currently starting, or has been stopped — it throws this ISE. It signals an illegal lifecycle state transition rather than a startup failure of the executor itself.

Source

Thrown at server/src/main/java/org/apache/druid/client/HttpServerInventoryView.java:157

  {
    this.httpClient = httpClient;
    this.smileMapper = smileMapper;
    this.druidNodeDiscoveryProvider = druidNodeDiscoveryProvider;
    this.defaultFilter = defaultFilter;
    this.finalPredicate = defaultFilter;
    this.config = config;
    this.serviceEmitter = serviceEmitter;
    this.executorFactory = executorFactory;
    this.execNamePrefix = execNamePrefix;
  }


  @LifecycleStart
  public void start()
  {
    synchronized (lifecycleLock) {
      if (!lifecycleLock.canStart()) {
        throw new ISE("Could not start lifecycle");
      }

      log.info("Starting executor[%s].", execNamePrefix);

      try {
        inventorySyncExecutor = executorFactory.create(
            config.getNumThreads(),
            execNamePrefix + "-%s"
        );
        monitoringExecutor = executorFactory.create(1, execNamePrefix + "-monitor-%s");

        DruidNodeDiscovery druidNodeDiscovery = druidNodeDiscoveryProvider.getForService(DataNodeService.DISCOVERY_SERVICE_KEY);
        druidNodeDiscovery.registerListener(
            new DruidNodeDiscovery.Listener()
            {
              private final AtomicBoolean initialized = new AtomicBoolean(false);

              @Override

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Ensure start() is called exactly once per instance; use a new instance instead of restarting the old one
  2. Check for double registration in guice module bindings or Lifecycle.addStartHook duplicates
  3. In tests, construct a fresh HttpServerInventoryView per test instead of reusing a static instance
  4. If a restart is required, stop() the instance first and verify lifecycleLock state before starting

Example fix

// before
view.start();
...
view.start(); // ISE: Could not start lifecycle
// after
if (!started) {
  view.start();
  started = true;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// guard
private final AtomicBoolean started = new AtomicBoolean(false);
if (!started.compareAndSet(false, true)) return;
view.start();

Try / catch

try { view.start(); } catch (IllegalStateException e) { log.warn("lifecycle already %s", e.getMessage()); }

Prevention

When it happens

Trigger: Calling start() twice without an intervening stop(); calling start() after stop() (lifecycle lock in stopped state); concurrent start() invocations racing on lifecycleLock.

Common situations: Registering the same HttpServerInventoryView instance with two Lifecycle objects (e.g. both server and test harness start it); wiring it into a guice lifecycle twice; restart logic that calls start() without reset of lifecycle state.

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