apache/druid · error · IllegalStateException
can't start.
Error message
can't start.
What it means
LookupReferencesManager.start(), annotated @LifecycleStart, uses a LifecycleLock; if canStart() returns false (already started, stop in progress, or stop already completed), it throws IllegalStateException 'can't start.' This enforces that the lookup manager transitions through its lifecycle exactly once and in order.
Source
Thrown at server/src/main/java/org/apache/druid/query/lookup/LookupReferencesManager.java:152
this.lookupSnapshotTaker = null;
} else {
this.lookupSnapshotTaker = new LookupSnapshotTaker(objectMapper, lookupConfig.getSnapshotWorkingDir());
}
this.coordinatorClient = coordinatorClient;
this.lookupListeningAnnouncerConfig = lookupListeningAnnouncerConfig;
this.lookupConfig = lookupConfig;
this.testMode = testMode;
this.lookupUpdateExecutorService = Execs.multiThreaded(
lookupConfig.getNumLookupLoadingThreads(),
"LookupExtractorFactoryContainerProvider-Update-%s"
);
}
@LifecycleStart
public void start() throws IOException
{
if (!lifecycleLock.canStart()) {
throw new ISE("can't start.");
}
try {
LOG.debug("LookupExtractorFactoryContainerProvider starting.");
if (!Strings.isNullOrEmpty(lookupConfig.getSnapshotWorkingDir())) {
FileUtils.mkdirp(new File(lookupConfig.getSnapshotWorkingDir()));
}
loadLookupsAndInitStateRef();
if (!testMode) {
mainThread = Execs.makeThread(
"LookupExtractorFactoryContainerProvider-MainThread",
() -> {
try {
if (!lifecycleLock.awaitStarted()) {
LOG.error("Lifecycle not started, lookup update notices will not be handled.");
return;
}
while (!Thread.interrupted() && lifecycleLock.awaitStarted(1, TimeUnit.MILLISECONDS)) {View on GitHub (pinned to 9b90983fd2)
Solutions
- Ensure start() is called exactly once per LookupReferencesManager instance via the Druid Lifecycle.
- Check logs/stack trace for a prior stop() call that invalidated the lifecycle lock.
- In tests, construct a fresh LookupReferencesManager per lifecycle cycle rather than reusing instances.
- Avoid calling start() from multiple threads; delegate to a single lifecycle coordinator.
Example fix
// before manager.stop(); manager.start(); // ISE: can't start. // after LookupReferencesManager manager = new LookupReferencesManager(...); // fresh instance manager.start();
Defensive patterns
Strategy: validation
Validate before calling
// call start() exactly once, e.g.
private final AtomicBoolean started = new AtomicBoolean(false);
if (started.compareAndSet(false, true)) { manager.start(); } Try / catch
try {
manager.start();
} catch (IllegalStateException e) {
// already started or stopped: treat as idempotent no-op or recreate instance
} Prevention
- Let the Druid Lifecycle own start/stop; avoid manual calls
- Never call start() after stop() on the same instance
- Use a fresh instance per test lifecycle cycle
When it happens
Trigger: Calling start() after the manager is already started; calling start() concurrently with or after stop(); lifecycle mis-wiring where a second component attempts to start the same instance.
Common situations: Double registration of LookupReferencesManager in a custom lifecycle; unit tests starting/stopping repeatedly without resetting; race between node shutdown and startup hooks.
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/006a26da68737a23.
Report an issue: GitHub.