apache/druid · error · IllegalStateException
Cannot load dimension order
Error message
Cannot load dimension order[%s] when existing index is not empty.
What it means
IncrementalIndex.loadDimensionList (via makeDimensionHandler) refuses to load a persisted dimension order when the index already contains rows. Dimension ordering can only be seeded into an empty index; applying it after rows were added would corrupt column ordering.
Solutions
- Only call loadDimensionList/loadDimensionIterable immediately after index creation, before any rows are added
- Create a new IncrementalIndex and load the dimension order into it before ingestion
- Reorder the code so dimension-order restoration precedes row ingestion
Example fix
// before index.add(row); index.loadDimensionIterable(oldOrder, oldFormats); // throws // after index.loadDimensionIterable(oldOrder, oldFormats); index.add(row);
Defensive patterns
Strategy: validation
Validate before calling
if (index.numRows() != 0) throw new IllegalStateException("Restore dimension order only on an empty index"); Try / catch
try { index.loadDimensionIterable(order, formats); } catch (ISE e) { if (e.getMessage().contains("not empty")) { /* recreate index */ } throw e; } Prevention
- Order all schema-loading calls before first add()
- Test recovery paths with non-empty indexes
When it happens
Trigger: Calling loadDimensionIterable / index recovery that supplies oldDimensionOrder while numRows() != 0, e.g. re-applying dimension order to a live IncrementalIndex that has already ingested events.
Common situations: Realtime task recovery code paths, custom ingestion code that calls loadDimensionList after rows were added, or mixing results from a persisted index with an in-use index.
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
- Attempt to add row to swapped-out sink for segment
- authResult.getErrorMessage()
- Bloom filter aggregators are query-time only
- Cache reference is null
- Can't find previous segmentIds for sequence
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/a8003457d9acc6d3.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndex.java:857
}
}
/**
* Currently called to initialize IncrementalIndex dimension order during index creation
* Index dimension ordering could be changed to initialize from DimensionsSpec after resolution of
* https://github.com/apache/druid/issues/2011
*
* @param oldDimensionOrder dimension order to initialize
* @param oldColumnFormats formats for the dimensions
*/
public void loadDimensionIterable(
Iterable<String> oldDimensionOrder,
Map<String, ColumnFormat> oldColumnFormats
)
{
synchronized (dimensionDescs) {
if (numRows() != 0) {
throw new ISE("Cannot load dimension order[%s] when existing index is not empty.", dimensionDescs.keySet());
}
for (String dim : oldDimensionOrder) {
// Skip __time; its position is solely based on configuration at index creation time.
if (!ColumnHolder.TIME_COLUMN_NAME.equals(dim) && dimensionDescs.get(dim) == null) {
ColumnFormat format = oldColumnFormats.get(dim);
addNewDimension(dim, format.getColumnHandler(dim));
}
}
}
}
@GuardedBy("dimensionDescs")
private DimensionDesc addNewDimension(String dim, DimensionHandler handler)
{
DimensionDesc desc = initDimension(dimensionDescs.size(), dim, handler);
dimensionDescs.put(dim, desc);
dimensionDescsList.add(desc);
return desc;View on GitHub (pinned to 9b90983fd2)