{"record":{"id":"1c244a62d9e8c53a","repo":"apache/druid","slug":"cannot-return-primitive-float-for-null-value","errorCode":null,"errorMessage":"Cannot return primitive float for Null Value","messagePattern":"Cannot return primitive float for Null Value","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/aggregation/NullableNumericAggregateCombiner.java","lineNumber":76,"sourceCode":"  @Override\n  public void fold(ColumnValueSelector selector)\n  {\n    boolean isNotNull = !selector.isNull();\n    if (isNotNull) {\n      if (isNullResult) {\n        isNullResult = false;\n        delegate.reset(selector);\n      } else {\n        delegate.fold(selector);\n      }\n    }\n  }\n\n  @Override\n  public float getFloat()\n  {\n    if (isNullResult) {\n      throw new IllegalStateException(\"Cannot return primitive float for Null Value\");\n    }\n    return delegate.getFloat();\n  }\n\n  @Override\n  public double getDouble()\n  {\n    if (isNullResult) {\n      throw new IllegalStateException(\"Cannot return double for Null Value\");\n    }\n    return delegate.getDouble();\n  }\n\n  @Override\n  public long getLong()\n  {\n    if (isNullResult) {\n      throw new IllegalStateException(\"Cannot return long for Null Value\");","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/aggregation/NullableNumericAggregateCombiner.java#L58-L94","documentation":"NullableNumericAggregateCombiner wraps a non-null-aware AggregateCombiner when SQL-compatible null handling is enabled. It tracks whether the combined result is null; since getFloat() returns a primitive float, there is no way to represent null, so it throws IllegalStateException rather than return a meaningless default (e.g. 0.0). Callers must check isNull() before calling the primitive getters.","triggerScenarios":"Calling getFloat() on a NullableNumericAggregateCombiner after reset() observed a null selector value and no subsequent fold() call supplied a non-null value, i.e. isNullResult == true. This happens when aggregation result extraction code reads a float directly without consulting isNull() first.","commonSituations":"Custom extension code or a custom SQL accessor reading aggregator results; all input rows for the aggregation group had null values under druid.generic.useDefaultValueForNull=false; a post-aggregation or result-format path that assumes primitives are always non-null.","solutions":["Call isNull() on the AggregateCombiner/ColumnValueSelector before invoking getFloat() and handle the null case (return null in the result row or skip).","Use getObject() instead of getFloat(); it returns null for null results instead of throwing.","If the caller cannot handle nulls, enable druid.generic.useDefaultValueForNull=true so nulls become 0-typed defaults, or coalesce the column in SQL (COALESCE).","Fix custom accessor code that mixes null-aware combiners with primitive-only extraction paths."],"exampleFix":"// before\nfloat value = combiner.getFloat();\n// after\nFloat value = combiner.isNull() ? null : combiner.getFloat();","handlingStrategy":"type-guard","validationCode":"// call-site guard: never read primitives from a null-aware combiner unchecked\nif (combiner.isNull()) {\n  return null; // or sentinel per your result model\n}","typeGuard":"static Float safeGetFloat(AggregateCombiner<?> combiner) {\n  return combiner.isNull() ? null : combiner.getFloat();\n}","tryCatchPattern":"try {\n  float v = combiner.getFloat();\n} catch (IllegalStateException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"Null Value\")) {\n    // treat as SQL NULL\n    return null;\n  }\n  throw e;\n}","preventionTips":["Always pair primitive getters (getFloat/getDouble/getLong) with an isNull() check on null-aware combiners.","Prefer getObject()/boxed accessors when writing result-extraction code that must handle SQL NULL.","Run with druid.generic.useDefaultValueForNull=false in CI to surface unguarded null paths in custom code.","COALESCE nullable columns in SQL when downstream consumers cannot represent null."],"tags":["druid","aggregation","null-handling","sql","state"],"backgroundTag":"null-argument","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"}