apache/druid · error · ExpressionValidationException

second argument should be STRING but got %s instead

Error message

second argument should be STRING but got %s instead

What it means

Thrown by Druid's time-formatting expression function when its optional second argument (the date format pattern) is not a STRING. The function builds a Joda-Time DateTimeFormat pattern from that argument, so a non-string type (LONG, DOUBLE, array, etc.) cannot be used and the expression is rejected at evaluation/validation time with a validation failure.

Source

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

    @Override
    public String name()
    {
      return "timestamp";
    }

    @Override
    public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings)
    {
      ExprEval value = args.get(0).eval(bindings);
      if (value.value() == null) {
        return ExprEval.ofLong(null);
      }

      DateTimes.UtcFormatter formatter = DateTimes.ISO_DATE_OPTIONAL_TIME;
      if (args.size() > 1) {
        ExprEval format = args.get(1).eval(bindings);
        if (!format.type().is(ExprType.STRING)) {
          throw validationFailed(
              "second argument should be STRING but got %s instead",
              format.type()
          );
        }
        formatter = DateTimes.wrapFormatter(DateTimeFormat.forPattern(format.asString()));
      }
      DateTime date;
      try {
        date = formatter.parse(value.asString());
      }
      catch (IllegalArgumentException e) {
        throw validationFailed(e, "invalid value %s", value.asString());
      }
      return toValue(date);
    }

    @Override
    public void validateArguments(List<Expr> args)

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Quote the format pattern as a string literal, e.g. time_format(ts, 'yyyy-MM-dd HH:mm:ss').
  2. Cast or convert the second argument to a string with CAST(... AS VARCHAR) / CONCAT before passing it.
  3. Check the argument type with the EXTRACT_TYPE or a query plan to confirm what type the expression yields.
  4. If the pattern is optional, remove the second argument entirely to use the default ISO_DATE_OPTIONAL_TIME formatter.

Example fix

// before
time_format(__time, yyyy-MM-dd)
// after
time_format(__time, 'yyyy-MM-dd')
Defensive patterns

Strategy: validation

Validate before calling

// Java caller-side check before building the expression
if (!(formatArg instanceof String)) {
    throw new IllegalArgumentException("time_format second argument must be a string pattern");
}

Type guard

// SQL: enforce via CAST
CAST(pattern_expr AS VARCHAR)

Try / catch

// wrap query submission; Druid surfaces this as an expression validation error
try { runQuery(q); } catch (ExpressionValidationException e) { /* fix format arg type */ }

Prevention

When it happens

Trigger: Calling the time format function with a second argument that evaluates to a non-STRING ExprType, e.g. time_format(ts, 1234) or passing a numeric column/literal instead of a quoted pattern like 'yyyy-MM-dd'.

Common situations: Copy-pasted SQL where the pattern string lost its quotes; passing a numeric format constant or a JSON object; translating queries from other engines where formats are numbers (e.g. strftime-style enums); typed columns in inputs that were inferred as LONG.

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