apache/druid · error · IllegalStateException
%s: %s, extractorID = %s
Error message
%s: %s, extractorID = %s
What it means
NamespaceLookupExtractorFactory.get() checks the CacheState of the scheduled cache entry. If the state is CacheScheduler.NoCache, the cache has been explicitly closed/evicted or failed with a recorded reason; get() throws this ISE embedding the entry, the noCache reason, and the extractor ID. It means the lookup existed but its backing cache is no longer usable.
Source
Thrown at extensions-core/lookups-cached-global/src/main/java/org/apache/druid/query/lookup/NamespaceLookupExtractorFactory.java:224
// 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();
return versionedCache.asLookupExtractor(isInjective(), cacheKey);
}
finally {
readLock.unlock();
}View on GitHub (pinned to 9b90983fd2)
Solutions
- Re-announce/restart the lookup so the cache is rescheduled and returns to a VersionedCache state.
- Check the noCache reason in the exception/logs (e.g. "closed" vs "failed") and fix the underlying cause (restore the data file, fix connectivity).
- Avoid deleting/overwriting the lookup while queries are running; coordinate lookup updates with query load.
- Handle this exception in query paths by retrying after lookup re-initialization.
Example fix
// before: delete then get lookupManager.delete(id); LookupExtractor l = factory.get(); // ISE: NoCache // after: get before teardown, or re-create first LookupExtractor l = factory.get(); lookupManager.delete(id);
Defensive patterns
Strategy: retry
Validate before calling
// java // no pre-call check exposed; verify lookup still announced boolean live = lookupManager.get(id) != null;
Try / catch
// java
try { LookupExtractor l = factory.get(); }
catch (IllegalStateException e) {
// parse noCache reason; re-announce lookup then retry
lookupManager.add(id, lookupConfig);
} Prevention
- Coordinate lookup deletes/updates with query traffic.
- Monitor CacheScheduler close/failed events.
- Keep underlying data files stable during lookups' poll period.
When it happens
Trigger: Calling get()/extractor() after CacheScheduler closed the entry (e.g. delete() called, cached file deleted, or cache closed due to an error), with NoCache states like "closed" or "failed"; racy get during lookup deletion (see testSimpleStartRacyGetDuringDelete).
Common situations: Lookup being updated/replaced concurrently while a query reads it; underlying remote file removed so cache refresh fails and the cache transitions to NoCache; shutdown of the lookup coordinator while queries are in flight.
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/26768ac80386914d.
Report an issue: GitHub.