apache/druid · error · IllegalStateException
Dimension[ ] occurred more than once in InputRow
Error message
Dimension[%s] occurred more than once in InputRow
What it means
IncrementalIndex.toIncrementalIndexRow throws IllegalStateException when the same dimension appears more than once in an InputRow. The index tracks per-row which dimensions have been processed (via the dims array); encountering a dimension again within one row would double-count values, so it fails fast. Rows with duplicate dimension keys violate the InputRow contract.
Solutions
- Deduplicate dimension names before constructing the InputRow
- Fix the parser/config so each dimension appears at most once per row
- Implement a custom InputRow wrapper that deduplicates getDimensions()
Example fix
// before
new MapBasedInputRow(timestamp, Arrays.asList("d1", "d2", "d1"), event); // duplicate "d1"
// after
List<String> dims = event.keySet().stream().distinct().collect(Collectors.toList());
new MapBasedInputRow(timestamp, dims, event); Defensive patterns
Strategy: validation
Validate before calling
Set<String> seen = new HashSet<>(); for (String d : row.getDimensions()) { if (!seen.add(d)) { throw new IllegalArgumentException("duplicate dimension in row: " + d); } } Try / catch
try { index.add(row); } catch (IllegalStateException e) { if (e.getMessage().contains("occurred more than once in InputRow")) { log.error("dropping malformed row", e); return -1; } throw e; } Prevention
- Deduplicate dimensions when constructing MapBasedInputRow
- Fix parser configs that can emit duplicate column names
- Add a schema check that dimension names are unique
When it happens
Trigger: Adding an InputRow whose getDimensions() contains the same dimension name twice (e.g. duplicate keys in a map-backed implementation, or custom InputRow implementations returning duplicates).
Common situations: Custom InputRow/MapBasedInputRow construction with duplicated dimension entries; parsers (e.g. from JSON with duplicate keys or CSV with repeated columns) producing duplicate dimension names; event flattening bugs.
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 find pushedSegments for segment
- Can't find segmentsForSequence for sequence
- Cannot coerce column
- ColumnCapacityExceededException
- Don't know how to handle type
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/7d52cbf519b93889.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndex.java:565
// unless this is the first row we are processing, all newly discovered columns will be sparse
if (maxIngestedEventTime != null) {
indexer.setSparseIndexed();
}
if (overflow == null) {
overflow = new ArrayList<>();
}
overflow.add(dimsKey);
} else if (desc.getIndex() > dims.length || dims[desc.getIndex()] != null) {
/*
* index > dims.length requires that we saw this dimension and added it to the dimensionOrder map,
* otherwise index is null. Since dims is initialized based on the size of dimensionOrder on each call to add,
* it must have been added to dimensionOrder during this InputRow.
*
* if we found an index for this dimension it means we've seen it already. If !(index > dims.length) then
* we saw it on a previous input row (this its safe to index into dims). If we found a value in
* the dims array for this index, it means we have seen this dimension already on this input row.
*/
throw new ISE("Dimension[%s] occurred more than once in InputRow", dimension);
} else {
dims[desc.getIndex()] = dimsKey;
}
}
// process any dimensions with missing values in the row
for (String missing : absentDimensions) {
dimensionDescs.get(missing).getIndexer().setSparseIndexed();
}
}
if (overflow != null) {
// Merge overflow and non-overflow
Object[] newDims = new Object[dims.length + overflow.size()];
System.arraycopy(dims, 0, newDims, 0, dims.length);
for (int i = 0; i < overflow.size(); ++i) {
newDims[dims.length + i] = overflow.get(i);
}View on GitHub (pinned to 9b90983fd2)