apache/druid · error · IllegalStateException

Selector must have a dictionary

Error message

Selector must have a dictionary

What it means

The per-dictionary-value cache in SingleStringInputCachingExpressionColumnValueSelector is sized by selector.getValueCardinality(); if the underlying dimension selector cannot report cardinality (CARDINALITY_UNKNOWN, e.g. a non-dictionary-encoded or grouped/unknown selector) the cache cannot be built and construction fails with ISE.

Source

Thrown at processing/src/main/java/org/apache/druid/segment/virtual/SingleStringInputCachingExpressionColumnValueSelector.java:74

      final Expr expression,
      @Nullable final RowIdSupplier rowIdSupplier
  )
  {
    super(rowIdSupplier);

    // Verify expression has just one binding.
    if (expression.analyzeInputs().getRequiredBindings().size() != 1) {
      throw new ISE("Expected expression with just one binding");
    }

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

    final Supplier<Object> inputSupplier = ExpressionSelectors.supplierFromDimensionSelector(selector, false, false);
    this.bindings = InputBindings.forInputSupplier(ExpressionType.STRING, inputSupplier);

    if (selector.getValueCardinality() == DimensionDictionarySelector.CARDINALITY_UNKNOWN) {
      throw new ISE("Selector must have a dictionary");
    } else if (selector.getValueCardinality() <= CACHE_SIZE) {
      arrayEvalCache = new ExprEval[selector.getValueCardinality()];
      lruEvalCache = null;
    } else {
      arrayEvalCache = null;
      lruEvalCache = new LruEvalCache(expression, bindings);
    }
  }

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

  @Override

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Use a dictionary-encoded dimension selector (nameLookupPossibleInAdvance with known cardinality)
  2. Force dictionary encoding for the column at ingestion time
  3. Use the deferred-evaluation (LruEvalCache / SingleStringInputDeferredEvaluationExpression*) selector instead, which doesn't need cardinality
  4. Check cardinality before choosing the caching selector in custom factory code

Example fix

// before
new SingleStringInputCachingExpressionColumnValueSelector(unknownCardinalitySelector, expr)
// after
if (sel.getValueCardinality() == CARDINALITY_UNKNOWN) {
  return new SingleStringInputDeferredEvaluationExpressionColumnValueSelector(sel, expr, ...);
}
Defensive patterns

Strategy: type-guard

Validate before calling

boolean cacheable = sel.getValueCardinality() != DimensionDictionarySelector.CARDINALITY_UNKNOWN;
return cacheable ? cachingSelector(sel, expr) : deferredSelector(sel, expr);

Type guard

boolean hasDictionary(DimensionDictionarySelector sel) {
  return sel.getValueCardinality() != CARDINALITY_UNKNOWN;
}

Try / catch

try {
  return new SingleStringInputCachingExpressionColumnValueSelector(sel, expr, ...);
} catch (ISE e) {
  return new SingleStringInputDeferredEvaluationExpressionColumnValueSelector(sel, expr, ...);
}

Prevention

When it happens

Trigger: Wrapping a string dimension selector whose getValueCardinality() returns DimensionDictionarySelector.CARDINALITY_UNKNOWN — typical with non-dictionary string columns, unknown cardinality selectors, or selectors that cannot do name lookup in advance.

Common situations: Running caching expression selectors over non-dictionary-encoded dimensions, or after ingestion changes where a column lost its dictionary encoding; also hits custom selector implementations that report unknown cardinality.

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/ad0e5303500723ba. Report an issue: GitHub.