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

  1. Deduplicate the input rows on the key columns before building the table
  2. Choose different key columns that uniquely identify each row
  3. Aggregate the rows (e.g. group by key and pick first/max) prior to indexing
  4. 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

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


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/b118cbc7ebd60214. Report an issue: GitHub.