apache/druid · error · UnsupportedOperationException

Cannot operate on a dimension with no dictionary

Error message

Cannot operate on a dimension with no dictionary

What it means

PooledTopNAlgorithm.makeInitParams requires a dictionary-encoded dimension selector; getValueCardinality() must return >= 0. A negative cardinality means the dimension has no dictionary (e.g. a string dimension backed by a non-dictionary or an uninitialized/foreign storage adapter), so the algorithm cannot allocate its int[cardinality] positions array. Druid throws UnsupportedOperationException because topN over an undictionary-able dimension is not supported in the pooled algorithm.

Source

Thrown at processing/src/main/java/org/apache/druid/query/topn/PooledTopNAlgorithm.java:234

  public PooledTopNAlgorithm(
      TopNQuery query,
      TopNCursorInspector cursorInspector,
      NonBlockingPool<ByteBuffer> bufferPool
  )
  {
    super(cursorInspector);
    this.query = query;
    this.bufferPool = bufferPool;
  }

  @Override
  public PooledTopNParams makeInitParams(ColumnSelectorPlus selectorPlus, Cursor cursor, CursorGranularizer granularizer)
  {
    final DimensionSelector dimSelector = (DimensionSelector) selectorPlus.getSelector();
    final int cardinality = dimSelector.getValueCardinality();

    if (cardinality < 0) {
      throw new UnsupportedOperationException("Cannot operate on a dimension with no dictionary");
    }

    final TopNMetricSpecBuilder<int[]> arrayProvider = new BaseArrayProvider<>(dimSelector, query, cursorInspector)
    {
      private final int[] positions = new int[cardinality];

      @Override
      public int[] build()
      {
        Pair<Integer, Integer> startEnd = computeStartEnd(cardinality);

        Arrays.fill(positions, 0, startEnd.lhs, SKIP_POSITION_VALUE);
        Arrays.fill(positions, startEnd.lhs, startEnd.rhs, INIT_POSITION_VALUE);
        Arrays.fill(positions, startEnd.rhs, positions.length, SKIP_POSITION_VALUE);

        return positions;
      }
    };

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Use a dictionary-encoded string column as the topN dimension
  2. Enable/verify dictionary encoding for the column at ingestion (string columns are dictionary-encoded by default)
  3. Rewrite the query as a groupBy if the dimension cannot have a dictionary or cardinality is unknown
  4. If using an expression dimension, ensure it produces dictionary-encoded values or switch to groupBy

Example fix

// before
topN(dimension: "concat(country, '-x')") // expression dim with no dictionary
// after
groupBy(dimension: "concat(country, '-x')") // groupBy supports unknown cardinality
Defensive patterns

Strategy: validation

Validate before calling

DimensionSelector selector = (DimensionSelector) selectorPlus.getSelector();
if (selector.getValueCardinality() < 0) {
  // fall back to groupBy or choose a dictionary-encoded dimension
}

Type guard

boolean hasDictionary(DimensionSelector selector) {
  return selector.getValueCardinality() >= 0;
}

Try / catch

try {
  return topNEngine.query(...);
} catch (UnsupportedOperationException e) {
  if (e.getMessage().contains("no dictionary")) { /* run groupBy instead */ }
  throw e;
}

Prevention

When it happens

Trigger: Running a topN query where the 'dimension' resolves to a selector whose getValueCardinality() returns -1 — typically a non-dictionary-encoded column, a virtual/expression dimension without dictionary support, or a segment whose string dictionary is unavailable.

Common situations: TopN on an expression/virtual column lacking a dictionary; querying segments written with dictionary-less string columns; misuse of topN where groupBy v2 should be used for high-cardinality or unencoded dimensions.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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