apache/druid · error · IllegalStateException
Factory [%s] not started
Error message
Factory [%s] not started
What it means
NamespaceLookupExtractorFactory.get() resolves the CacheScheduler entry for the extractor ID; if the entry is null the factory was never successfully started (start() not called, still awaiting initialization, or it failed/closed). It throws this ISE rather than returning a lookup backed by no cache, per LookupExtractorFactory contract that get() requires a started factory.
Source
Thrown at extensions-core/lookups-cached-global/src/main/java/org/apache/druid/query/lookup/NamespaceLookupExtractorFactory.java:219
public boolean isInjective()
{
return injective;
}
// Grab the latest snapshot from the CacheScheduler's entry
@Override
public LookupExtractor get()
{
final Lock readLock = startStopSync.readLock();
try {
readLock.lockInterruptibly();
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
if (entry == null) {
throw new ISE("Factory [%s] not started", extractorID);
}
final CacheScheduler.CacheState cacheState = entry.getCacheState();
if (cacheState instanceof CacheScheduler.NoCache) {
final String noCacheReason = ((CacheScheduler.NoCache) cacheState).name();
throw new ISE("%s: %s, extractorID = %s", entry, noCacheReason, extractorID);
}
CacheScheduler.VersionedCache versionedCache = (CacheScheduler.VersionedCache) cacheState;
final byte[] v = StringUtils.toUtf8(versionedCache.getVersion());
final byte[] id = StringUtils.toUtf8(extractorID);
final byte injectiveByte = isInjective() ? (byte) 1 : (byte) 0;
final Supplier<byte[]> cacheKey = () ->
ByteBuffer
.allocate(CLASS_CACHE_KEY.length + id.length + 1 + v.length + 1 + 1)
.put(CLASS_CACHE_KEY)
.put(id).put((byte) 0xFF)
.put(v).put((byte) 0xFF)
.put(injectiveByte)
.array();View on GitHub (pinned to 9b90983fd2)
Solutions
- Call start() (and await its completion / use awaitInitialization) before invoking get().
- Verify the lookup's underlying data source is reachable so start() can finish populating the cache.
- Check lookup manager logs for a failed start or an earlier close() on this extractor ID; re-announce the lookup.
- In code, guard with isStarted()/lifecycle checks before calling get().
Example fix
// before
LookupExtractor lookup = factory.get();
// after
if (!factory.start()) { throw new ISE("lookup %s failed to start", id); }
factory.awaitInitialization();
LookupExtractor lookup = factory.get(); Defensive patterns
Strategy: validation
Validate before calling
// java
if (!factory.isStarted()) { factory.start(); }
// await initialization before get()
factory.awaitInitialization(clock, 30_000); Try / catch
// java
try { LookupExtractor l = factory.get(); }
catch (IllegalStateException e) {
if (e.getMessage().startsWith("Factory [") ) { /* restart/await start */ }
} Prevention
- Always awaitInitialization after start() before serving lookups.
- Ensure lookup data sources are reachable at startup.
- Watch logs for failed start() on namespace lookups.
When it happens
Trigger: Calling get() (directly or via extractor()) before start() completes; racing start() against a close/delete; factory failed during background cache loading so entry never got set.
Common situations: Lookup announced to the cluster while its underlying data (e.g. remote CSV/DB) is unreachable, so initialization never completes; tests querying lookups immediately after config load; racy lookup replacement where a delete lands between start and get.
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
- %s: %s, extractorID = %s
- Cache reference is null
- Null queryRunner! Looks to be some segment unmapping action
- Already started
- can't start.
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/457e038aa397c80c.
Report an issue: GitHub.