{"record":{"id":"74d1566bf4d14411","repo":"apache/druid","slug":"unsupported-type","errorCode":null,"errorMessage":"unsupported type ","messagePattern":"unsupported type ","errorType":"exception","errorClass":"IAE","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/math/expr/UnaryOperatorExpr.java","lineNumber":146,"sourceCode":"\n  @Override\n  public ExprEval eval(ObjectBinding bindings)\n  {\n    if (expr instanceof BigIntegerExpr) {\n      // Special case to handle unary minus for Long.MIN_VALUE: converting the literal to long directly is impossible\n      return ExprEval.of(((BigInteger) expr.getLiteralValue()).multiply(BigInteger.valueOf(-1)).longValueExact());\n    }\n    ExprEval ret = expr.eval(bindings);\n    if (ret.value() == null) {\n      return ExprEval.ofType(ret.type(), null);\n    }\n    if (ret.type().is(ExprType.LONG)) {\n      return ExprEval.of(-ret.asLong());\n    }\n    if (ret.type().is(ExprType.DOUBLE)) {\n      return ExprEval.of(-ret.asDouble());\n    }\n    throw new IAE(\"unsupported type \" + ret.type());\n  }\n\n  @Override\n  public boolean canVectorize(InputBindingInspector inspector)\n  {\n    return inspector.areNumeric(expr) && expr.canVectorize(inspector);\n  }\n\n  @Override\n  public <T> ExprVectorProcessor<T> asVectorProcessor(VectorInputBindingInspector inspector)\n  {\n    return VectorMathProcessors.negate().asProcessor(inspector, expr);\n  }\n}\n\n@SuppressWarnings(\"ClassName\")\nclass UnaryNotExpr extends UnaryExpr\n{","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/math/expr/UnaryOperatorExpr.java#L128-L164","documentation":"UnaryOperatorExpr.Neg evaluates its operand and negates it; it only supports LONG and DOUBLE result types. If the operand's ExprEval type is neither (e.g. STRING or an array type), IAE 'unsupported type <type>' is thrown at evaluation time.","triggerScenarios":"Evaluating a negation expression like \"-x\" where x resolves to a non-numeric type — a string column (even numeric-looking, e.g. '-5' as STRING), a NULL-only/string-typed virtual column, or an array-typed input.","commonSituations":"Negating columns ingested as strings from CSV/Kafka without explicit numeric type declarations; applying '-' to a timestamp/STRING cast result; schema drift where a column changed from numeric to string.","solutions":["Cast the operand to a numeric type before negating: \"-CAST(x AS LONG)\" or \"-CAST(x AS DOUBLE)\".","Fix the ingestion spec so the column is declared LONG/DOUBLE instead of STRING.","Guard non-numeric rows with a filter or COALESCE/default value before applying unary minus."],"exampleFix":"// before\n\"-x\" // x is STRING\n// after\n\"-CAST(x AS DOUBLE)\"","handlingStrategy":"type-guard","validationCode":"Expr.BindingAnalysis a = expr.analyzeInputs();\n// ensure inputs are numeric-typed, or wrap operand in CAST(x AS DOUBLE)","typeGuard":"// in expression form: only negate after an explicit numeric cast\n\"-CAST(x AS DOUBLE)\" // or check output type before negating in code\nif (!ret.type().is(ExprType.LONG) && !ret.type().is(ExprType.DOUBLE)) { /* coerce or reject */ }","tryCatchPattern":"try { result = eval(negExpr, bindings); } catch (IllegalArgumentException e) { if (e.getMessage().startsWith(\"unsupported type\")) { result = eval(negWithCast, bindings); } }","preventionTips":["Declare numeric columns as LONG/DOUBLE in ingestion specs, not STRING","Always CAST before arithmetic on possibly-string inputs","Watch for schema drift turning numeric columns into strings"],"tags":["expression","type-mismatch","unary-operator","druid"],"backgroundTag":"type-mismatch","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"}