{"record":{"id":"8db992ece56cc91d","repo":"apache/druid","slug":"unrecognized-unary-operator-s","errorCode":null,"errorMessage":"Unrecognized unary operator %s","messagePattern":"Unrecognized unary operator (.+?)","errorType":"validation","errorClass":"RE","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/math/expr/ExprListenerImpl.java","lineNumber":83,"sourceCode":"\n  Expr getAST()\n  {\n    return (Expr) nodes.get(rootNodeKey);\n  }\n\n  @Override\n  public void exitUnaryOpExpr(ExprParser.UnaryOpExprContext ctx)\n  {\n    int opCode = ((TerminalNode) ctx.getChild(0)).getSymbol().getType();\n    switch (opCode) {\n      case ExprParser.MINUS:\n        nodes.put(ctx, new UnaryMinusExpr(ctx.getChild(0).getText(), (Expr) nodes.get(ctx.getChild(1))));\n        break;\n      case ExprParser.NOT:\n        nodes.put(ctx, new UnaryNotExpr(ctx.getChild(0).getText(), (Expr) nodes.get(ctx.getChild(1))));\n        break;\n      default:\n        throw new RE(\"Unrecognized unary operator %s\", ctx.getChild(0).getText());\n    }\n  }\n\n  @Override\n  public void exitApplyFunctionExpr(ExprParser.ApplyFunctionExprContext ctx)\n  {\n    String fnName = ctx.getChild(0).getText();\n    // Built-in functions.\n    final ApplyFunction function = Parser.getApplyFunction(fnName);\n    if (function == null) {\n      throw new RE(\"function '%s' is not defined.\", fnName);\n    }\n\n    nodes.put(\n        ctx,\n        new ApplyFunctionExpr(function, fnName, (LambdaExpr) nodes.get(ctx.lambda()), (List<Expr>) nodes.get(ctx.fnArgs()))\n    );\n  }","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/math/expr/ExprListenerImpl.java#L65-L101","documentation":"During ANTLR-based expression parsing, ExprListenerImpl.exitUnaryOpExpr maps the parse-tree operator token to UnaryMinusExpr or UnaryNotExpr. Any other unary operator token reaches the default branch and throws a RuntimeException ('Unrecognized unary operator %s'), indicating the parser grammar and the listener are out of sync or the input produced an unexpected token.","triggerScenarios":"Parsing an expression whose unary operator token isn't '-' or 'NOT', typically via Expr.parse / Parser.parse with malformed or nonstandard unary syntax.","commonSituations":"Hand-written expressions with invalid unary syntax like '!' (instead of NOT) or stray symbols; grammar/parser version skew after a Druid upgrade; programmatic query builders emitting unsupported tokens.","solutions":["Fix the expression string to use supported unary operators: '-' for negation and 'NOT' for logical not","Replace '!' with 'NOT' or restructure the condition","If the token looks valid, upgrade Druid so grammar and listener match"],"exampleFix":"// before\n\"!flag\"\n// after\n\"NOT flag\"","handlingStrategy":"validation","validationCode":"// validate expression text before parsing\nString trimmed = exprStr.trim();\nif (trimmed.matches(\"^!.*\") || trimmed.startsWith(\"~\")) {\n  throw new IllegalArgumentException(\"unsupported unary operator in: \" + exprStr);\n}","typeGuard":null,"tryCatchPattern":"try {\n  expr = Expr.parse(exprStr);\n} catch (RuntimeException e) {\n  if (e.getMessage() != null && e.getMessage().startsWith(\"Unrecognized unary operator\")) {\n    throw new IllegalArgumentException(\"Invalid expression: \" + exprStr, e);\n  } else { throw e; }\n}","preventionTips":["Use only '-' and 'NOT' for unary operators in native expressions","Lint/validate user-supplied expressions before parsing","Pin grammar and listener versions when customizing Druid"],"tags":["expressions","parsing","grammar"],"backgroundTag":"invalid-argument-format","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"}