{"record":{"id":"c17b951dc0f904e0","repo":"apache/druid","slug":"output-type-s-must-be-numeric","errorCode":null,"errorMessage":"Output type[%s] must be numeric","messagePattern":"Output type\\[(.+?)\\] must be numeric","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/data/input/Rows.java","lineNumber":122,"sourceCode":"   * @param inputValue           the actual object being converted\n   * @param outputType           expected return type, or null if it should be automatically detected\n   * @param throwParseExceptions whether this method should throw a {@link ParseException} or use a default/null value\n   *                             when {@param inputValue} is not numeric\n   *\n   * @return a Number; will not necessarily be the same type as {@param zeroClass}\n   *\n   * @throws ParseException if the input cannot be converted to a number and {@code throwParseExceptions} is true\n   */\n  @Nullable\n  public static Number objectToNumber(\n      final String name,\n      final Object inputValue,\n      @Nullable final ValueType outputType,\n      final boolean throwParseExceptions\n  )\n  {\n    if (outputType != null && !outputType.isNumeric()) {\n      throw new IAE(\"Output type[%s] must be numeric\", outputType);\n    }\n\n    final Number asNumber;\n\n    if (inputValue == null) {\n      asNumber = null;\n    } else if (inputValue instanceof Number) {\n      asNumber = (Number) inputValue;\n    } else if (inputValue instanceof String) {\n      try {\n        String metricValueString = StringUtils.removeChar(((String) inputValue).trim(), ',');\n        // Longs.tryParse() doesn't support leading '+', so we need to trim it ourselves\n        metricValueString = trimLeadingPlusOfLongString(metricValueString);\n\n        Number v = null;\n\n        // Try parsing as Long first, since it's significantly faster than Double parsing, and also there are various\n        // integer numbers that can be represented as Long but cannot be represented as Double.","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/data/input/Rows.java#L104-L140","documentation":"Rows.objectToNumber validates that the requested outputType is a numeric ValueType (LONG, FLOAT, DOUBLE) before converting a field value to a Number. Druid throws this IllegalArgumentException immediately when a caller passes a non-numeric output type such as STRING or COMPLEX, because the method's contract is to produce numbers only. It is a programming/API misuse error, not a data error.","triggerScenarios":"Calling Rows.objectToNumber(name, inputValue, ValueType.STRING, true) (or any non-numeric ValueType) directly; passing a column type expression or metric type spec that resolves to a non-numeric ValueType into a numeric aggregator/extraction path that delegates to objectToNumber.","commonSituations":"Aggregator specs where the type is mistakenly set to \"string\" while using floatSum/longSum style aggregation; custom code copying the 3-arg overload and adding an explicit outputType; type specs auto-derived from a dimension whose type was configured incorrectly.","solutions":["Change the outputType argument to a numeric ValueType: ValueType.LONG, ValueType.FLOAT, or ValueType.DOUBLE.","Pass null for outputType to have Druid auto-detect the numeric type from the input value.","If a string is genuinely needed, use Rows.objectToStrings instead of objectToNumber."],"exampleFix":"// before\nNumber n = Rows.objectToNumber(\"metric\", inputValue, ValueType.STRING, true);\n// after\nNumber n = Rows.objectToNumber(\"metric\", inputValue, ValueType.DOUBLE, true);","handlingStrategy":"type-guard","validationCode":"if (outputType != null && !outputType.isNumeric()) {\n  throw new IllegalArgumentException(\"outputType must be numeric: \" + outputType);\n}","typeGuard":"boolean isNumericOutput(@Nullable ValueType t) {\n  return t == null || t == ValueType.LONG || t == ValueType.FLOAT || t == ValueType.DOUBLE;\n}","tryCatchPattern":"try {\n  return Rows.objectToNumber(name, value, outputType, true);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"must be numeric\")) {\n    outputType = null; // fall back to auto-detect\n    return Rows.objectToNumber(name, value, null, true);\n  }\n  throw e;\n}","preventionTips":["Only pass LONG, FLOAT, DOUBLE, or null as outputType to objectToNumber.","Prefer the 3-arg overload (auto-detect) unless an explicit numeric type is required.","Derive outputType from the aggregator's declared type and assert it is numeric at spec load time."],"tags":["java","illegal-argument","type-mismatch","druid-rows"],"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-14T05:17:10.506Z"}