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
- Quote the format pattern as a string literal, e.g. time_format(ts, 'yyyy-MM-dd HH:mm:ss').
- Cast or convert the second argument to a string with CAST(... AS VARCHAR) / CONCAT before passing it.
- Check the argument type with the EXTRACT_TYPE or a query plan to confirm what type the expression yields.
- 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
- Always quote date format patterns as string literals
- Dry-run/EXPLAIN queries before execution
- Keep format patterns in constants, not computed values
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
- Cannot implicitly cast [%s] to [%s]
- Cannot get time from type[%s]
- second argument must be a base64 encoded STRING value but go
- Cannot cast [%s] to [%s] (Types.InvalidCastException from in
- invalid value %s
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/2b532b6b8949379a.
Report an issue: GitHub.