apache/druid · error · ExpressionValidationException

does not accept types

Error message

does not accept %s types

What it means

A variadic expression function (GREATEST/LEAST-style accumulator) type-checks every argument against isValidType; any argument whose expression type is not accepted aborts the whole expression with this validation error naming the offending type.

Solutions

  1. Cast offending arguments to a valid type: CAST(col AS BIGINT) or CAST(col AS DOUBLE)
  2. Check the function's isValidType (numeric-only here) and remove non-numeric arguments
  3. Inspect column types via EXPLAIN PLAN; string-typed dimensions need explicit numeric casts
  4. Fix the ingestion schema so the column carries the intended numeric type

Example fix

// before: GREATEST(dim, 10) where dim is STRING -> throws
// after: GREATEST(CAST(dim AS BIGINT), 10)
Defensive patterns

Strategy: type-guard

Validate before calling

for (ExpressionType t : argTypes) {
  if (!(t.is(ExprType.LONG) || t.is(ExprType.DOUBLE))) {
    throw new IllegalArgumentException("numeric args required, got " + t);
  }
}

Type guard

static boolean allNumeric(List<ExpressionType> types) {
  return types.stream().allMatch(t -> t.is(ExprType.LONG) || t.is(ExprType.DOUBLE));
}

Prevention

When it happens

Trigger: Calling the function with a STRING, complex, or array argument when it accepts only numeric types, e.g. greatest('abc', 1).

Common situations: Mixed-type operands where one is a string-typed dimension; schema drift after ingestion changes a column from numeric to string; hand-written native expressions in filters/selectors.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/math/expr/Function.java:671

    }

    @Override
    public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings)
    {
      if (args.isEmpty()) {
        return ExprEval.ofLong(null);
      }

      // evaluate arguments and collect output type
      List<ExprEval<?>> evals = new ArrayList<>();
      ExpressionType outputType = ExpressionType.LONG;

      for (Expr expr : args) {
        ExprEval<?> exprEval = expr.eval(bindings);
        ExpressionType exprType = exprEval.type();

        if (!isValidType(exprType)) {
          throw validationFailed("does not accept %s types", exprType);
        }
        outputType = ExpressionTypeConversion.function(outputType, exprType);

        if (exprEval.value() != null) {
          evals.add(exprEval);
        }
      }

      if (evals.isEmpty()) {
        // The GREATEST/LEAST functions are not in the SQL standard. Emulate the behavior of postgres (return null if
        // all expressions are null, otherwise skip null values) since it is used as a base for a wide number of
        // databases. This also matches the behavior the long/double greatest/least post aggregators. Some other
        // databases (e.g., MySQL) return null if any expression is null.
        // https://www.postgresql.org/docs/9.5/functions-conditional.html
        // https://dev.mysql.com/doc/refman/8.0/en/comparison-operators.html#function_least
        return ExprEval.ofType(outputType, null);
      }

View on GitHub (pinned to 9b90983fd2)