apache/druid · warning
Projected output type
Error message
Projected output type %s of expression %s does not match provided type %s
What it means
ExpressionVirtualColumn.capabilities compares the output type inferred by evaluating the Druid expression against the explicitly configured outputType. If they differ and neither side is numeric, it warns that the projected type does not match the configured one; the inferred type is used. For numeric mismatches it only logs at debug level.
Solutions
- Align the column's outputType with the expression's inferred type
- Update the expression so its result type matches the declared outputType
- Treat the warning as informational if numeric (inference still wins for numerics)
- Cast inside the expression (e.g. CAST(... AS VARCHAR)) to force the intended type
Example fix
// before // ExpressionVirtualColumn.forExpression(DruidExpression.of(ColumnType.STRING, expr), "v0", null) where expr returns LONG // after // ExpressionVirtualColumn.forExpression(DruidExpression.of(ColumnType.LONG, expr), "v0", null) // or cast: DruidExpression.of(ColumnType.STRING, "CAST(expr AS VARCHAR)")
Defensive patterns
Strategy: validation
Validate before calling
// Before configuring, infer the expression type and compare with declared outputType
ColumnType inferred = Expressions.inferType(factory, parsedExpr);
if (declaredOutputType != null && inferred != null && inferred.getType() != declaredOutputType.getType()) { /* fix spec */ } Type guard
null
Try / catch
null
Prevention
- Keep outputType consistent with the expression's result type
- Add explicit CAST in the expression when a specific output type is required
- Re-check virtual column specs after editing expressions
When it happens
Trigger: An ExpressionVirtualColumn is configured with outputType (e.g. STRING) but the expression's inferred type differs (e.g. LONG or ARRAY), discovered when capabilities() is called during query planning.
Common situations: A user-specified outputType in the virtual column spec contradicts the expression, e.g. after editing the expression to return an array while keeping the old outputType, or a planner change altering inference.
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
- attempt to get boolean[] null vector from string[] only…
- attempt to get double[] from string[] only scalar binding
- attempt to get long[] from string[] only scalar binding
- Only constant and single input string expressions currently…
- unsupported type
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/e2ee49ad8ab299c6.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/segment/virtual/ExpressionVirtualColumn.java:312
@Override
public ColumnCapabilities capabilities(ColumnInspector inspector, String columnName)
{
if (isDirectAccess(inspector)) {
return inspector.getColumnCapabilities(expression.parsed.get().getBindingIfIdentifier());
}
final ColumnType outputType = expression.outputType;
final ExpressionPlan plan = ExpressionPlanner.plan(inspector, expression.parsed.get());
final ColumnCapabilities inferred = plan.inferColumnCapabilities(outputType);
// if we can infer the column capabilities from the expression plan, then use that
if (inferred != null) {
// explicit outputType is used as a hint, how did it compare to the planners inferred output type?
if (outputType != null && inferred.getType() != outputType.getType()) {
// if both sides are numeric, let it slide and log at debug level
// but mismatches involving strings and arrays might be worth knowing about so warn
if (!inferred.isNumeric() && !outputType.isNumeric()) {
log.warn(
"Projected output type %s of expression %s does not match provided type %s",
inferred.asTypeString(),
expression.expressionString,
outputType
);
} else {
log.debug(
"Projected output type %s of expression %s does not match provided type %s",
inferred.asTypeString(),
expression.expressionString,
outputType
);
}
}
return inferred;
}
// fallback to default capabilitiesView on GitHub (pinned to 9b90983fd2)