apache/druid · error · IllegalStateException
Cannot get cache: %s
Error message
Cannot get cache: %s
What it means
CacheScheduler.Entry.getCache() returns the materialized lookup map only while the entry's CacheState is a VersionedCache (a successfully loaded snapshot). If the entry is in any other state — e.g. it was deleted, or the cache creation failed — there is no cache to return and this ISE is thrown with the current state.
Source
Thrown at extensions-core/lookups-cached-global/src/main/java/org/apache/druid/server/lookup/namespace/cache/CacheScheduler.java:104
* Returns the last cache state, either {@link NoCache} or {@link VersionedCache}.
*/
public CacheState getCacheState()
{
return impl.cacheStateHolder.get();
}
/**
* @return the entry's cache if it is already initialized and not yet closed
* @throws IllegalStateException if the entry's cache is not yet initialized, or {@link #close()} has
* already been called
*/
public Map<String, String> getCache()
{
CacheState cacheState = getCacheState();
if (cacheState instanceof VersionedCache) {
return ((VersionedCache) cacheState).getCache();
} else {
throw new ISE("Cannot get cache: %s", cacheState);
}
}
@VisibleForTesting
Future<?> getUpdaterFuture()
{
return impl.updaterFuture;
}
@VisibleForTesting
public void awaitTotalUpdates(int totalUpdates) throws InterruptedException
{
impl.updateCounter.awaitCount(totalUpdates);
}
@VisibleForTesting
public void awaitTotalUpdatesWithTimeout(int totalUpdates, long timeoutMills)
throws InterruptedException, TimeoutExceptionView on GitHub (pinned to 9b90983fd2)
Solutions
- Check entry lifecycle before reading: only call getCache() on a live, scheduled entry.
- Use the Entry's cacheFuture/getUpdaterFuture or await the update future before reading the cache.
- Fix code to re-fetch the entry from the scheduler after delete/replace instead of caching the reference.
Example fix
// before Entry entry = scheduler.schedule(ns); scheduler.delete(entry); Map<String,String> m = entry.getCache(); // ISE // after Entry entry = scheduler.schedule(ns); Map<String,String> m = entry.getCache(); // read while live scheduler.delete(entry);
Defensive patterns
Strategy: try-catch
Validate before calling
if (entry.getCacheState() instanceof VersionedCache) { entry.getCache(); } Type guard
boolean readable(Entry e) { return e.getCacheState() instanceof VersionedCache; } Try / catch
try { return entry.getCache(); } catch (IllegalStateException e) { if (e.getMessage().startsWith("Cannot get cache:")) { return Collections.emptyMap(); } throw e; } Prevention
- Await the entry's update future before reading the cache
- Never reuse Entry references after scheduler.delete(entry)
- Re-fetch entries from the scheduler instead of caching them
When it happens
Trigger: Calling entry.getCache() after the entry has been closed/deleted via scheduler.delete(entry), or before/since an update replaced the versioned snapshot with a non-VersionedCache state.
Common situations: Concurrent lookup deletion while another thread reads the cache; using a stale Entry reference after shutdown; tests driving entry lifecycle without waiting for load completion.
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
- can't start.
- %s: %s, extractorID = %s
- cache [%s] is already attached
- Expected WeakReference, got: %s
- can't stop.
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/26a594e95defa0ab.
Report an issue: GitHub.