{"record":{"id":"d6d0aabe4d1ecf58","repo":"apache/druid","slug":"unknown-operation-s-known-operations-s","errorCode":null,"errorMessage":"Unknown operation[%s], known operations[%s]","messagePattern":"Unknown operation\\[(.+?)\\], known operations\\[(.+?)\\]","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/aggregation/post/ArithmeticPostAggregator.java","lineNumber":89,"sourceCode":"      @JsonProperty(\"fn\") String fnName,\n      @JsonProperty(\"fields\") List<PostAggregator> fields,\n      @JsonProperty(\"ordering\") @Nullable String ordering\n  )\n  {\n    Preconditions.checkArgument(fnName != null, \"fn cannot not be null\");\n    Preconditions.checkArgument(\n        fields != null && fields.size() > 1,\n        \"Illegal number of fields[%s], must be > 1\",\n        fields.size()\n    );\n\n    this.name = name;\n    this.fnName = fnName;\n    this.fields = fields;\n\n    this.op = Ops.lookup(fnName);\n    if (op == null) {\n      throw new IAE(\"Unknown operation[%s], known operations[%s]\", fnName, Ops.getFns());\n    }\n\n    this.ordering = ordering;\n    this.comparator = ordering == null ? DEFAULT_COMPARATOR : Ordering.valueOf(ordering);\n  }\n\n  @Override\n  public Set<String> getDependentFields()\n  {\n    Set<String> dependentFields = new HashSet<>();\n    for (PostAggregator field : fields) {\n      dependentFields.addAll(field.getDependentFields());\n    }\n    return dependentFields;\n  }\n\n  @Override\n  public Comparator getComparator()","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/aggregation/post/ArithmeticPostAggregator.java#L71-L107","documentation":"ArithmeticPostAggregator's constructor resolves fnName through Ops.lookup and throws IllegalArgumentException when the operation name is not one of the known arithmetic ops (+, -, *, /, quotient). This fails fast at query/segment setup rather than producing wrong results.","triggerScenarios":"Constructing ArithmeticPostAggregator with fnName set to an unrecognized string (e.g. \"divide\", \"%\", \"plus\", or a null/typo) in a JSON query spec or programmatic construction.","commonSituations":"Typos in hand-written native query JSON (e.g. \"minus\" vs \"-\"), migrating queries from SQL expressions to native post-aggregators, or generating specs dynamically with unvalidated operator input.","solutions":["Use one of the known operator strings exactly: +, -, *, /, or quotient","Check Ops.getFns() (shown in the exception message) for the accepted set","For division with double semantics prefer \"/\"; use \"quotient\" only for integer-style division behavior","Validate operator names before deserializing user-supplied query JSON"],"exampleFix":"// before\nnew ArithmeticPostAggregator(\"ratio\", \"divide\", fields, null);\n// after\nnew ArithmeticPostAggregator(\"ratio\", \"/\", fields, null);","handlingStrategy":"validation","validationCode":"java.util.Set<String> known = Set.of(\"+\",\"-\",\"*\",\"/\",\"quotient\");\nif (fnName == null || !known.contains(fnName)) throw new IllegalArgumentException(\"bad op: \" + fnName);","typeGuard":"boolean isValidArithOp(String fn) { return fn != null && Set.of(\"+\",\"-\",\"*\",\"/\",\"quotient\").contains(fn); }","tryCatchPattern":"try { new ArithmeticPostAggregator(name, fnName, fields, ordering); } catch (IllegalArgumentException e) { /* fix fnName per message's known operations */ }","preventionTips":["Copy operator names from official docs, not SQL keywords","Log Ops.getFns() output when generating specs dynamically","Validate user-supplied query JSON against known ops before submission"],"tags":["java","query","post-aggregator","validation"],"backgroundTag":"invalid-enum-value","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}