apache/druid · error · IllegalStateException
Not initialized. If this is the first lookup, post an empty…
Error message
Not initialized. If this is the first lookup, post an empty map to initialize
What it means
LookupCoordinatorManager.updateLookups refuses to apply a non-empty lookup update when the coordinator's cache of known lookups is not yet initialized (getKnownLookups() returned null). This guard prevents a client from accidentally wiping all existing lookup configuration because the manager simply hasn't loaded it yet. The first update must be an empty map to explicitly initialize the lookup state.
Solutions
- POST an empty JSON object {} to /druid/coordinator/v1/lookups (with the tier/layer envelope) once to initialize.
- Then POST the real lookup specs in a second request.
- If the coordinator was restarted and should already be initialized, check why getKnownLookups() is null (config load errors in logs).
Example fix
// before
client.post("/druid/coordinator/v1/lookups", myLookupSpecs);
// after
client.post("/druid/coordinator/v1/lookups", "{}"); // initialize once
client.post("/druid/coordinator/v1/lookups", myLookupSpecs); // then update Defensive patterns
Strategy: validation
Validate before calling
// Initialize once before any lookup updates:
await fetch(`${coordinator}/druid/coordinator/v1/lookups`, {
method: 'POST', body: '{}'
}).then(r => { if (!r.ok) throw new Error('lookup init failed'); }); Type guard
function isInitialized(knownLookups) {
return knownLookups !== null && knownLookups !== undefined;
} Try / catch
try {
await updateLookups(specs);
} catch (ISE e) {
if (e.getMessage().startsWith("Not initialized")) {
await postEmptyInitMap();
await updateLookups(specs);
} else { throw e; }
} Prevention
- Always perform the one-time empty-map POST to /druid/coordinator/v1/lookups before pushing real lookup specs.
- In automation, add an idempotent 'ensure initialized' step that posts {} when the manager is fresh.
- Check coordinator logs for lookup config load failures after restarts that can leave the cache null.
When it happens
Trigger: POSTing lookup specs to /druid/coordinator/v1/lookups (or calling updateLookups) before any initialization POST of an empty map {} has been made, so priorSpec is null while updateSpec is non-empty.
Common situations: Fresh cluster or fresh lookup tier where operators push their lookup tables without the required initial empty-map POST; coordinator restarted and config load failed leaving cache null; automation scripts skipping the init step.
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
- is not started
- given update for lookup
- LookupCoordinatorManager can't start.
- Cache reference is null
- Can't find surrogate task
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/fa11245aa5e2114f.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/main/java/org/apache/druid/server/lookup/cache/LookupCoordinatorManager.java:208
return true;
}
//ensure all the lookups specs have version specified. ideally this should be done in the LookupExtractorFactoryMapContainer
//constructor but that allows null to enable backward compatibility with 0.10.0 lookup specs
for (final Map.Entry<String, Map<String, LookupExtractorFactoryMapContainer>> tierEntry : updateSpec.entrySet()) {
for (Map.Entry<String, LookupExtractorFactoryMapContainer> e : tierEntry.getValue().entrySet()) {
Preconditions.checkNotNull(
e.getValue().getVersion(),
"lookup [%s]:[%s] does not have version.", tierEntry.getKey(), e.getKey()
);
}
}
synchronized (this) {
final Map<String, Map<String, LookupExtractorFactoryMapContainer>> priorSpec = getKnownLookups();
if (priorSpec == null && !updateSpec.isEmpty()) {
// To prevent accidentally erasing configs if we haven't updated our cache of the values
throw new ISE("Not initialized. If this is the first lookup, post an empty map to initialize");
}
final Map<String, Map<String, LookupExtractorFactoryMapContainer>> updatedSpec;
// Only add or update here, don't delete.
if (priorSpec == null) {
// all new
updatedSpec = updateSpec;
} else {
// Needs update
updatedSpec = new HashMap<>(priorSpec);
for (final Map.Entry<String, Map<String, LookupExtractorFactoryMapContainer>> tierEntry : updateSpec.entrySet()) {
final String tier = tierEntry.getKey();
final Map<String, LookupExtractorFactoryMapContainer> updateTierSpec = tierEntry.getValue();
final Map<String, LookupExtractorFactoryMapContainer> priorTierSpec = priorSpec.get(tier);
if (priorTierSpec == null) {
// New tier
updatedSpec.put(tier, updateTierSpec);View on GitHub (pinned to 9b90983fd2)