{"record":{"id":"06a2623c3b35102e","repo":"apache/druid","slug":"rank-must-be-a-number","errorCode":null,"errorMessage":"rank must be a number","messagePattern":"rank must be a number","errorType":"validation","errorClass":"DruidException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/expression/ArrayQuantileExprMacro.java","lineNumber":67,"sourceCode":"  private static final String RANK_ARG_NAME = \"rank\";\n\n  @Override\n  public String name()\n  {\n    return FN_NAME;\n  }\n\n  @Override\n  public Expr apply(final List<Expr> args)\n  {\n    validationHelperCheckArgumentCount(args, 2);\n\n    final Expr arg = args.get(0);\n    final Expr rankArg = args.get(1);\n\n    validationHelperCheckArgIsLiteral(rankArg, RANK_ARG_NAME);\n    if (!(rankArg.getLiteralValue() instanceof Number)) {\n      throw validationFailed(\"%s must be a number\", RANK_ARG_NAME);\n    }\n\n    final double rank = ((Number) rankArg.getLiteralValue()).doubleValue();\n\n    class ArrayQuantileExpr extends ExprMacroTable.BaseScalarMacroFunctionExpr\n    {\n      private ArrayQuantileExpr(List<Expr> args)\n      {\n        super(ArrayQuantileExprMacro.this, args);\n      }\n\n      @Nonnull\n      @Override\n      public ExprEval<?> eval(final ObjectBinding bindings)\n      {\n        final DoubleList doubles = toDoubleArray(arg.eval(bindings));\n\n        if (doubles == null) {","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/expression/ArrayQuantileExprMacro.java#L49-L85","documentation":"The ARRAY_QUANTILE expression macro requires its second argument ('rank') to be a literal number. Druid throws this ExprMacroTable validation error during expression compilation when the rank argument is a literal of a non-numeric type (e.g. a string), so the quantile function can never be evaluated.","triggerScenarios":"Calling ARRAY_QUANTILE(expr, '0.5') in a Druid SQL/expression selector where the rank is quoted as a string; passing a non-literal (column reference) as rank; passing a literal boolean or null as the second argument.","commonSituations":"Hand-written SQL with quoted percentile values ('0.9'); generated SQL from BI tools that quotes all function arguments; users expecting the rank to be a column instead of a constant.","solutions":["Remove quotes from the rank argument so it is a numeric literal: ARRAY_QUANTILE(expr, 0.5).","Ensure the rank argument is a literal constant, not a column or nested expression.","Cast/convert the value to a number in the calling code before building the expression."],"exampleFix":"-- before\nSELECT ARRAY_QUANTILE(arr, '0.5') FROM t\n-- after\nSELECT ARRAY_QUANTILE(arr, 0.5) FROM t","handlingStrategy":"validation","validationCode":"// JS/TS before emitting SQL\nif (typeof rank !== 'number' || Number.isNaN(rank)) throw new Error('ARRAY_QUANTILE rank must be a numeric literal');","typeGuard":"function isNumericLiteral(x) { return typeof x === 'number' && Number.isFinite(x); }","tryCatchPattern":"try {\n  return runDruidQuery(query);\n} catch (ExpressionValidationException e) {\n  if (e.getMessage().contains(\"rank must be a number\")) {\n    throw new UserInputException(\"Quantile rank must be an unquoted number, e.g. 0.5\");\n  }\n  throw e;\n}","preventionTips":["Never quote numeric arguments in generated Druid expressions.","Keep quantile ranks as constants, not column references.","Validate user-provided percentiles in the application layer before building queries."],"tags":["druid","expression","validation","argument-type"],"backgroundTag":"invalid-argument-value","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"}