{"record":{"id":"5395f1171aa42bef","repo":"apache/druid","slug":"cannot-return-float-for-null-value-5395f1","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/NullableNumericBufferAggregator.java","lineNumber":93,"sourceCode":"      delegate.aggregate(buf, position + Byte.BYTES);\n    }\n  }\n\n  @Override\n  @Nullable\n  public Object get(ByteBuffer buf, int position)\n  {\n    if (buf.get(position) == TypeStrategies.IS_NULL_BYTE) {\n      return null;\n    }\n    return delegate.get(buf, position + Byte.BYTES);\n  }\n\n  @Override\n  public float getFloat(ByteBuffer buf, int position)\n  {\n    if (buf.get(position) == TypeStrategies.IS_NULL_BYTE) {\n      throw new IllegalStateException(\"Cannot return float for Null Value\");\n    }\n    return delegate.getFloat(buf, position + Byte.BYTES);\n  }\n\n  @Override\n  public long getLong(ByteBuffer buf, int position)\n  {\n    if (buf.get(position) == TypeStrategies.IS_NULL_BYTE) {\n      throw new IllegalStateException(\"Cannot return long for Null Value\");\n    }\n    return delegate.getLong(buf, position + Byte.BYTES);\n  }\n\n  @Override\n  public double getDouble(ByteBuffer buf, int position)\n  {\n    if (buf.get(position) == TypeStrategies.IS_NULL_BYTE) {\n      throw new IllegalStateException(\"Cannot return double for Null Value\");","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/aggregation/NullableNumericBufferAggregator.java#L75-L111","documentation":"NullableNumericBufferAggregator stores a null-flag byte alongside each aggregated value in an off-heap ByteBuffer. getFloat() checks that flag (TypeStrategies.IS_NULL_BYTE) at the given position and throws IllegalStateException if the value is null, because returning a primitive float would silently coerce null to 0.0f.","triggerScenarios":"Calling getFloat(buf, position) where the null-marker byte at `position` equals TypeStrategies.IS_NULL_BYTE — the group's aggregated value was never set to a non-null value.","commonSituations":"GroupBy v8 / buffer-based aggregation results where a group key exists but all its input rows had null values; buffer reorganization/relocation bugs that misalign the null-flag byte; custom buffer aggregators that don't reserve the extra null byte.","solutions":["Call isNull(buf, position) first and skip getFloat() when it returns true","Ensure the buffer aggregator reserves one extra byte (Byte.BYTES) for the null flag and reads the delegate at position + Byte.BYTES","Check TypeStrategies/init() writes the correct null-marker byte for every newly allocated group slot","Verify buffer relocation (relocate()) copies the null flag along with the value"],"exampleFix":"// before\nfloat v = agg.getFloat(buf, position);\n// after\nfloat v = agg.isNull(buf, position) ? 0.0f /* or handle null */ : agg.getFloat(buf, position);","handlingStrategy":"validation","validationCode":"// before reading from the buffer\nif (agg.isNull(buf, position)) {\n  return null; // or default\n}","typeGuard":null,"tryCatchPattern":"try {\n  return agg.getFloat(buf, position);\n} catch (IllegalStateException e) {\n  if (e.getMessage().contains(\"Cannot return float for Null Value\")) {\n    return null;\n  }\n  throw e;\n}","preventionTips":["Reserve the extra Byte.BYTES null-flag slot in buffer allocations and delegate at position + Byte.BYTES","Initialize the null-marker byte for every new group position via TypeStrategies/init()","Copy the marker byte during buffer relocation, not just the value","Test with groups whose input rows are all null"],"tags":["java","aggregation","bytebuffer","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"}