{"record":{"id":"ad0e5303500723ba","repo":"apache/druid","slug":"selector-must-have-a-dictionary","errorCode":null,"errorMessage":"Selector must have a dictionary","messagePattern":"Selector must have a dictionary","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/segment/virtual/SingleStringInputCachingExpressionColumnValueSelector.java","lineNumber":74,"sourceCode":"      final Expr expression,\n      @Nullable final RowIdSupplier rowIdSupplier\n  )\n  {\n    super(rowIdSupplier);\n\n    // Verify expression has just one binding.\n    if (expression.analyzeInputs().getRequiredBindings().size() != 1) {\n      throw new ISE(\"Expected expression with just one binding\");\n    }\n\n    this.selector = Preconditions.checkNotNull(selector, \"selector\");\n    this.expression = Preconditions.checkNotNull(expression, \"expression\");\n\n    final Supplier<Object> inputSupplier = ExpressionSelectors.supplierFromDimensionSelector(selector, false, false);\n    this.bindings = InputBindings.forInputSupplier(ExpressionType.STRING, inputSupplier);\n\n    if (selector.getValueCardinality() == DimensionDictionarySelector.CARDINALITY_UNKNOWN) {\n      throw new ISE(\"Selector must have a dictionary\");\n    } else if (selector.getValueCardinality() <= CACHE_SIZE) {\n      arrayEvalCache = new ExprEval[selector.getValueCardinality()];\n      lruEvalCache = null;\n    } else {\n      arrayEvalCache = null;\n      lruEvalCache = new LruEvalCache(expression, bindings);\n    }\n  }\n\n  @Override\n  public void inspectRuntimeShape(final RuntimeShapeInspector inspector)\n  {\n    super.inspectRuntimeShape(inspector);\n    inspector.visit(\"selector\", selector);\n    inspector.visit(\"expression\", expression);\n  }\n\n  @Override","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/segment/virtual/SingleStringInputCachingExpressionColumnValueSelector.java#L56-L92","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use a dictionary-encoded dimension selector (nameLookupPossibleInAdvance with known cardinality)","Force dictionary encoding for the column at ingestion time","Use the deferred-evaluation (LruEvalCache / SingleStringInputDeferredEvaluationExpression*) selector instead, which doesn't need cardinality","Check cardinality before choosing the caching selector in custom factory code"],"exampleFix":"// before\nnew SingleStringInputCachingExpressionColumnValueSelector(unknownCardinalitySelector, expr)\n// after\nif (sel.getValueCardinality() == CARDINALITY_UNKNOWN) {\n  return new SingleStringInputDeferredEvaluationExpressionColumnValueSelector(sel, expr, ...);\n}","handlingStrategy":"type-guard","validationCode":"boolean cacheable = sel.getValueCardinality() != DimensionDictionarySelector.CARDINALITY_UNKNOWN;\nreturn cacheable ? cachingSelector(sel, expr) : deferredSelector(sel, expr);","typeGuard":"boolean hasDictionary(DimensionDictionarySelector sel) {\n  return sel.getValueCardinality() != CARDINALITY_UNKNOWN;\n}","tryCatchPattern":"try {\n  return new SingleStringInputCachingExpressionColumnValueSelector(sel, expr, ...);\n} catch (ISE e) {\n  return new SingleStringInputDeferredEvaluationExpressionColumnValueSelector(sel, expr, ...);\n}","preventionTips":["Check getValueCardinality() before choosing caching vs deferred selectors","Ensure columns are dictionary-encoded when relying on dictionary-based optimizations","Handle non-dictionary columns in custom selector factories"],"tags":["druid","expression","dictionary-encoding","vectorized-query"],"backgroundTag":"invalid-argument-value","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}