apache/druid · error · IllegalStateException

can't start.

Error message

can't start.

What it means

K8sDruidNodeDiscoveryProvider.start() throws this IllegalStateException when the internal LifecycleLock refuses the start request. The lock only allows a start when the instance is currently in the stopped/not-started state and no start is already in progress. This guards against initializing Kubernetes watchers twice, which would leak executor threads and duplicate node announcements.

Solutions

  1. Ensure start() is called exactly once per instance, e.g. from a Druid Lifecycle entry point only
  2. In tests, stop the provider in an @AfterEach/cleanup and create a fresh instance per test
  3. Check that the prior start() did not throw before lifecycleLock.succeededStart() completed - if it did, the instance is unusable; construct a new one
  4. If restart is needed, create a new K8sDruidNodeDiscoveryProvider instead of re-calling start() on a stopped instance

Example fix

// before
provider.start();
provider.start(); // throws ISE("can't start.")
// after
if (!started) {
  provider.start();
  started = true;
}
Defensive patterns

Strategy: validation

Validate before calling

if (providerStarted) { throw new IllegalStateException("provider already started"); }

Try / catch

try { provider.start(); started = true; } catch (IllegalStateException e) { log.warn("start refused: %s", e.getMessage()); }

Prevention

When it happens

Trigger: Calling start() twice on the same provider instance; calling start() concurrently from two threads so both pass between checks; calling start() after stop() (lock does not allow restart); a prior start() threw mid-way leaving the lock in a bad state.

Common situations: Druid extension lifecycle double-injects the provider as both a Lifecycle.Start and a service; tests calling start() in @BeforeEach without stopping in @AfterEach; misconfigured Guice multibindings instantiating two providers where one lifecycle manages both.

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

Appendix: source

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

              podInfo,
              discoveryConfig,
              k8sApiClient,
              watcherErrorRetryWaitMS
          );
          if (startAfterCreation) {
            nodeRoleWatcher.start();
          }
          LOGGER.info("Created NodeRoleWatcher for role[%s].", nType);
          return nodeRoleWatcher;
        }
    );
  }

  @LifecycleStart
  public void start()
  {
    if (!lifecycleLock.canStart()) {
      throw new ISE("can't start.");
    }

    try {
      LOGGER.info("starting");

      // This is single-threaded to ensure that all listener calls are executed precisely in the oder of add/remove
      // event occurences.
      listenerExecutor = Execs.scheduledSingleThreaded("K8sDruidNodeDiscoveryProvider-ListenerExecutor");

      LOGGER.info("started");

      lifecycleLock.started();
    }
    finally {
      lifecycleLock.exitStart();
    }
  }

View on GitHub (pinned to 9b90983fd2)