{"record":{"id":"16f35a331cf21eb6","repo":"apache/druid","slug":"cannot-return-float-for-null-value","errorCode":null,"errorMessage":"Cannot return float for Null Value","messagePattern":"Cannot return float for Null Value","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/aggregation/NullableNumericAggregator.java","lineNumber":83,"sourceCode":"      delegate.aggregate();\n    }\n  }\n\n  @Override\n  @Nullable\n  public Object get()\n  {\n    if (isNullResult) {\n      return null;\n    }\n    return delegate.get();\n  }\n\n  @Override\n  public float getFloat()\n  {\n    if (isNullResult) {\n      throw new IllegalStateException(\"Cannot return float for Null Value\");\n    }\n    return delegate.getFloat();\n  }\n\n  @Override\n  public long getLong()\n  {\n    if (isNullResult) {\n      throw new IllegalStateException(\"Cannot return long for Null Value\");\n    }\n    return delegate.getLong();\n  }\n\n  @Override\n  public double getDouble()\n  {\n    if (isNullResult) {\n      throw new IllegalStateException(\"Cannot return double for Null Value\");","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/aggregation/NullableNumericAggregator.java#L65-L101","documentation":"NullableNumericAggregator wraps an underlying aggregator and tracks whether the aggregated result is null via isNullResult. Druid treats a null numeric result as a distinct state, so calling getFloat() while the result is null would silently return 0.0f, hiding the null. This IllegalStateException is thrown instead to force callers to check isNull() before reading a primitive value.","triggerScenarios":"Calling getFloat() on a NullableNumericAggregator whose delegate aggregator produced no non-null values (isNullResult == true), typically during result materialization of an aggregation over a column where every row was null.","commonSituations":"Queries aggregating (sum/min/max/count of numerics) over a column that is entirely null or missing in the segment; custom code iterating aggregation results without consulting aggregator.isNull(); SQL-layer regressions where null handling was expected but the vectorized/non-vectorized path mismatched.","solutions":["Call aggregator.isNull() first and only call getFloat() when it returns false","Check that the query/column should really be non-null; a fully-null input column means the result is legitimately null","If implementing a wrapper, ensure isNullResult is reset in reset()/init() so stale null state does not persist","Upgrade/verify Druid version — mismatches between null-handling in the query engine and the aggregator have been fixed over time"],"exampleFix":"// before\nfloat v = agg.getFloat();\n// after\nFloat v = agg.isNull() ? null : agg.getFloat();","handlingStrategy":"type-guard","validationCode":"// before extracting a primitive\nif (agg.isNull()) {\n  return null; // or a default\n}","typeGuard":"Float value = agg.isNull() ? null : agg.getFloat();","tryCatchPattern":"try {\n  return agg.getFloat();\n} catch (IllegalStateException e) {\n  if (e.getMessage().contains(\"Cannot return float for Null Value\")) {\n    return null; // treat as null result\n  }\n  throw e;\n}","preventionTips":["Always call isNull() before getFloat/getLong/getDouble on nullable aggregators","Remember Druid null semantics: an all-null input column yields a null aggregate, not 0","When writing custom aggregators, keep isNullResult correctly initialized and reset","Test aggregation queries against segments where the target column is entirely null"],"tags":["java","aggregation","null-handling"],"backgroundTag":"internal-invariant-violation","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"}