apache/druid · error · IllegalStateException
Lookup[%s] is not loaded
Error message
Lookup[%s] is not loaded
What it means
When an MSQ stage reads from a lookup datasource, LookupInputSliceReader.attach fetches the lookup's segments through a segment wrangler. If the wrangler returns zero segments, the lookup data is not loaded anywhere in the cluster, so reading is impossible and this ISE is thrown naming the lookup.
Source
Thrown at multi-stage-query/src/main/java/org/apache/druid/msq/input/lookup/LookupInputSliceReader.java:77
@Override
public PhysicalInputSlice attach(
final int inputNumber,
final InputSlice slice,
final CounterTracker counters,
final Consumer<Throwable> warningPublisher
)
{
final String lookupName = ((LookupInputSlice) slice).getLookupName();
final Iterable<Segment> segments =
segmentWrangler.getSegmentsForIntervals(
new LookupDataSource(lookupName),
Intervals.ONLY_ETERNITY
);
final Iterator<Segment> segmentIterator = segments.iterator();
if (!segmentIterator.hasNext()) {
throw new ISE("Lookup[%s] is not loaded", lookupName);
}
final Segment segment = segmentIterator.next();
if (segmentIterator.hasNext()) {
// LookupSegmentWrangler always returns zero or one segments, so this code block can't
// happen. That being said: we'll program defensively anyway.
CloseableUtils.closeAndSuppressExceptions(
segment,
e -> log.warn(e, "Failed to close segment for lookup[%s]", lookupName)
);
throw new ISE("Lookup[%s] has multiple segments; cannot read", lookupName);
}
final LoadableSegment loadableSegment = AdaptedLoadableSegment.fromUnmanagedSegment(
segment,
new SegmentDescriptor(Intervals.ETERNITY, "0", 0),
StringUtils.format("lookup[%s]", lookupName),
counters.channel(CounterNames.inputChannel(inputNumber))View on GitHub (pinned to 9b90983fd2)
Solutions
- Verify the lookup name exists in cluster lookup configuration (GET /druid/coordinator/v1/lookups/status).
- Load or re-populate the lookup by posting the lookup config to /druid/coordinator/v1/lookups and waiting for it to report loaded on all nodes.
- Check lookup node logs for load failures (memory limits, TieredLookupConfig errors).
- Fix typos in the LOOKUP datasource name in the SQL query.
Example fix
// before
SELECT * FROM src WHERE v = LOOKUP(dim, 'missing_lookup')
// after: ensure the lookup is loaded first, e.g. via coordinator API
// POST /druid/coordinator/v1/lookups/config with "__default": {"missing_lookup": {...}}
SELECT * FROM src WHERE v = LOOKUP(dim, 'missing_lookup') Defensive patterns
Strategy: validation
Validate before calling
LookupStatus status = client.lookupStatus(lookupName); // coordinator API
if (status == null || !status.isLoaded()) {
throw new IllegalStateException("Lookup not loaded: " + lookupName);
} Try / catch
try {
reader.attach(buffer);
} catch (IllegalStateException e) {
if (e.getMessage() != null && e.getMessage().contains("is not loaded")) {
throw new QueryException("Lookup not loaded: " + lookupName + "; check /druid/coordinator/v1/lookups/status", e);
}
throw e;
} Prevention
- Verify lookup status via the coordinator API before running LOOKUP queries.
- Avoid lookup name typos by centralizing lookup names in config.
- Monitor lookup load failures on lookup-enabled tiers.
When it happens
Trigger: attach() is called for a lookupName whose LookupSegmentWrangler response contains no segments - i.e. the lookup is configured on the cluster but no node currently holds a loaded LookupSegment for it.
Common situations: Query references a lookup name with a typo; lookup was configured but never populated (no init script/entry loaded); lookup was dropped or its load failed after a broker restart; lookup config replication lag on a fresh cluster.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Lookup[%s] has multiple segments; cannot read
- Cannot find kernel corresponding to stage [%s] in query [%s]
- Failed to stream logs from: %s
- IOException wrapping underlying cause
- User [%s] does not exist.
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/cbf391aca8ba04b4.
Report an issue: GitHub.