apache/druid · error · IllegalStateException

Selector of class[%s] does not have a dictionary, cannot use

Error message

Selector of class[%s] does not have a dictionary, cannot use it.

What it means

SingleStringInputDeferredEvaluationExpressionDimensionSelector requires the wrapped dimension selector to expose a usable dictionary: known value cardinality and name lookup possible in advance. Selectors without a dictionary (CARDINALITY_UNKNOWN or nameLookupPossibleInAdvance()==false) cannot back deferred expression evaluation, so an ISE naming the offending class is thrown.

Source

Thrown at processing/src/main/java/org/apache/druid/segment/virtual/SingleStringInputDeferredEvaluationExpressionDimensionSelector.java:62

 *
 * @see SingleStringInputDeferredEvaluationExpressionDimensionVectorSelector for the vectorized version of
 * this selector.
 */
public class SingleStringInputDeferredEvaluationExpressionDimensionSelector implements DimensionSelector
{
  private final DimensionSelector selector;
  private final Expr expression;
  private final SingleInputBindings bindings = new SingleInputBindings(ExpressionType.STRING);

  public SingleStringInputDeferredEvaluationExpressionDimensionSelector(
      final DimensionSelector selector,
      final Expr expression
  )
  {
    // Verify selector has a working dictionary.
    if (selector.getValueCardinality() == DimensionDictionarySelector.CARDINALITY_UNKNOWN
        || !selector.nameLookupPossibleInAdvance()) {
      throw new ISE("Selector of class[%s] does not have a dictionary, cannot use it.", selector.getClass().getName());
    }

    this.selector = Preconditions.checkNotNull(selector, "selector");
    this.expression = Preconditions.checkNotNull(expression, "expression");
  }

  @Override
  public void inspectRuntimeShape(final RuntimeShapeInspector inspector)
  {
    inspector.visit("selector", selector);
    inspector.visit("expression", expression);
  }

  /**
   * Get the underlying selector {@link IndexedInts} row
   */
  @Override
  public IndexedInts getRow()

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Provide a dictionary-encoded dimension selector for the input column
  2. Re-encode the column with dictionary encoding at ingestion
  3. Use a selector path that does not require dictionary lookup (e.g. value-selector based expression evaluation)
  4. Call getValueCardinality() and nameLookupPossibleInAdvance() before construction to guard

Example fix

// before
new SingleStringInputDeferredEvaluationExpressionDimensionSelector(nonDictSelector, expr)
// after
Preconditions.checkArgument(sel.nameLookupPossibleInAdvance(), "need dictionary selector");
new SingleStringInputDeferredEvaluationExpressionDimensionSelector(dictSelector, expr);
Defensive patterns

Strategy: type-guard

Validate before calling

boolean usable = sel.getValueCardinality() != CARDINALITY_UNKNOWN && sel.nameLookupPossibleInAdvance();

Type guard

boolean isDictionarySelector(DimensionDictionarySelector sel) {
  return sel.getValueCardinality() != CARDINALITY_UNKNOWN && sel.nameLookupPossibleInAdvance();
}

Try / catch

try {
  return new SingleStringInputDeferredEvaluationExpressionDimensionSelector(sel, expr);
} catch (ISE e) {
  // fall back to a value-selector based expression evaluation path
  return fallbackExpressionProcessor(expr, sel);
}

Prevention

When it happens

Trigger: Constructor called with a selector where getValueCardinality() == CARDINALITY_UNKNOWN or nameLookupPossibleInAdvance() is false — e.g. non-dictionary-encoded strings, virtual/unknown-cardinality selectors.

Common situations: Applying expression virtual columns on top of non-dimension (row-based) selectors, query planning mixing grouped results with expression selectors, or custom DimensionSelector implementations lacking dictionaries.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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