apache/druid · error · IllegalStateException

Cache reference is null

Error message

Cache reference is null

What it means

PollingLookup.apply() reads from an internal AtomicReference<CacheRefKeeper> that holds the current cache. When the lookup has been closed (or not yet initialized), the reference is null and the method throws IllegalStateException instead of silently returning null. This is an internal invariant: a live lookup must always have a cache reference.

Source

Thrown at extensions-core/lookups-cached-single/src/main/java/org/apache/druid/server/lookup/PollingLookup.java:119

        scheduledExecutorService.shutdown();
      }
      CacheRefKeeper cacheRefKeeper = refOfCacheKeeper.getAndSet(null);
      if (cacheRefKeeper != null) {
        cacheRefKeeper.doneWithIt();
      }
    }
  }

  @Override
  @Nullable
  public String apply(@Nullable String key)
  {
    if (key == null) {
      return null;
    }
    final CacheRefKeeper cacheRefKeeper = refOfCacheKeeper.get();
    if (cacheRefKeeper == null) {
      throw new ISE("Cache reference is null");
    }
    final PollingCache cache = cacheRefKeeper.getAndIncrementRef();
    try {
      if (cache == null) {
        // it must've been closed after swapping while I was getting it.  Try again.
        return this.apply(key);
      }
      return (String) cache.get(key);
    }
    finally {
      if (cache != null) {
        cacheRefKeeper.doneWithIt();
      }
    }
  }

  @Override
  public List<String> unapply(@Nullable final String value)

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Refresh lookup configuration so all tasks/brokers have the lookup defined, then retry the query
  2. Check for concurrent use of the lookup after close() in your code; stop using the lookup once closed
  3. Upgrade Druid if you hit this during normal lookup swaps (known race windows were fixed over time)

Example fix

// before
String val = pollingLookup.apply(key);
// after
if (!pollingLookup.isOpen()) { return null; }
String val = pollingLookup.apply(key);
Defensive patterns

Strategy: try-catch

Validate before calling

if (lookup == null || !lookup.isOpen()) { /* skip or refresh lookup config */ }

Type guard

boolean usable(PollingLookup l) { return l != null && l.isOpen(); }

Try / catch

try { return lookup.apply(key); } catch (IllegalStateException e) { if (e.getMessage().contains("Cache reference is null")) { return fallbackValue; } throw e; }

Prevention

When it happens

Trigger: Calling apply(key) after PollingLookup.close() has been called, or applying a lookup while it is being swapped/replaced during a lookup manager refresh race.

Common situations: A query references a lookup that was just removed or updated through the lookup configuration API; stale query plans holding old lookup objects; concurrent close-and-query during cluster config propagation.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/7cf30523a8668893. Report an issue: GitHub.