apache/druid · error · IllegalStateException
Expected single element
Error message
Expected single element
What it means
During index building, after grouping rows by key value, each key is expected to map to exactly one row number for the compressed single-row index layout. A key mapping to multiple rows violates this invariant and RowBasedIndexBuilder.build throws IllegalStateException.
Source
Thrown at processing/src/main/java/org/apache/druid/segment/join/table/RowBasedIndexBuilder.java:159
final long rangeThreshold = Math.max(
INT_ARRAY_SMALL_SIZE_OK,
Math.min(Integer.MAX_VALUE, INT_ARRAY_SPACE_SAVINGS_FACTOR * index.size())
);
if (range > 0 && range < rangeThreshold) {
final int[] indexAsArray = new int[Ints.checkedCast(range)];
Arrays.fill(indexAsArray, IndexedTable.Index.NOT_FOUND);
// Safe to cast to Long2ObjectMap because the constructor always uses one for long-typed keys.
final ObjectIterator<Long2ObjectMap.Entry<IntSortedSet>> entries =
((Long2ObjectMap<IntSortedSet>) ((Map) index)).long2ObjectEntrySet().iterator();
while (entries.hasNext()) {
final Long2ObjectMap.Entry<IntSortedSet> entry = entries.next();
final IntSortedSet rowNums = entry.getValue();
if (rowNums.size() != 1) {
throw new ISE("Expected single element");
}
indexAsArray[Ints.checkedCast(entry.getLongKey() - minLongKey)] = rowNums.firstInt();
entries.remove();
}
assert index.isEmpty();
// Early return of specialized implementation.
return new UniqueLongArrayIndex(indexAsArray, minLongKey);
}
}
return new MapIndex(keyType, index, nullIndex, nonNullKeysUnique);
}
}
View on GitHub (pinned to 9b90983fd2)
Solutions
- Deduplicate the input rows on the key columns before building the table
- Choose different key columns that uniquely identify each row
- Aggregate the rows (e.g. group by key and pick first/max) prior to indexing
- Verify upstream data for accidental duplicates
Example fix
// before
rows.stream() // may contain duplicate keys
.collect(RowBasedIndexedTable.build(...));
// after
Collection<Row> deduped = rows.stream()
.collect(Collectors.toMap(r -> r.get("id"), r -> r, (a, b) -> a))
.values();
RowBasedIndexedTable.build(deduped, ...); Defensive patterns
Strategy: validation
Validate before calling
boolean keysUnique = rows.stream().map(r -> r.get(keyCol)).distinct().count() == rows.size();
Try / catch
try { table = builder.build(rows, cacheKey); } catch (IllegalStateException e) { /* deduplicate rows and retry */ } Prevention
- Deduplicate on key columns before building the indexed table
- Verify key uniqueness with a pre-pass over the data
When it happens
Trigger: Building an indexed table (RowBasedIndexedTable.build / index) where a key-column value occurs in more than one row, i.e. duplicate key rows in the input, when the builder's uniqueness expectation is violated in the long-key compaction path.
Common situations: Feeding non-unique data into a table declared with key columns (an implicit 'unique key' assumption); duplicate rows from repeated ingestion; building a lookup-style table from a denormalized dataset.
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
- No such stage[%s]
- Writable channel is not available. The output channel might
- Frame allocator is not available. The output channel might b
- Cannot have both awaitChannels and awaitFutures
- Can't add [%d, %s] to non-empty SingleEntryShort2ObjectSorte
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/b118cbc7ebd60214.
Report an issue: GitHub.