apache/druid · error · IllegalStateException

Cannot return primitive float for Null Value

Error message

Cannot return primitive float for Null Value

What it means

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.

Source

Thrown at processing/src/main/java/org/apache/druid/query/aggregation/NullableNumericAggregateCombiner.java:76

  @Override
  public void fold(ColumnValueSelector selector)
  {
    boolean isNotNull = !selector.isNull();
    if (isNotNull) {
      if (isNullResult) {
        isNullResult = false;
        delegate.reset(selector);
      } else {
        delegate.fold(selector);
      }
    }
  }

  @Override
  public float getFloat()
  {
    if (isNullResult) {
      throw new IllegalStateException("Cannot return primitive float for Null Value");
    }
    return delegate.getFloat();
  }

  @Override
  public double getDouble()
  {
    if (isNullResult) {
      throw new IllegalStateException("Cannot return double for Null Value");
    }
    return delegate.getDouble();
  }

  @Override
  public long getLong()
  {
    if (isNullResult) {
      throw new IllegalStateException("Cannot return long for Null Value");

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Call isNull() on the AggregateCombiner/ColumnValueSelector before invoking getFloat() and handle the null case (return null in the result row or skip).
  2. Use getObject() instead of getFloat(); it returns null for null results instead of throwing.
  3. If the caller cannot handle nulls, enable druid.generic.useDefaultValueForNull=true so nulls become 0-typed defaults, or coalesce the column in SQL (COALESCE).
  4. Fix custom accessor code that mixes null-aware combiners with primitive-only extraction paths.

Example fix

// before
float value = combiner.getFloat();
// after
Float value = combiner.isNull() ? null : combiner.getFloat();
Defensive patterns

Strategy: type-guard

Validate before calling

// call-site guard: never read primitives from a null-aware combiner unchecked
if (combiner.isNull()) {
  return null; // or sentinel per your result model
}

Type guard

static Float safeGetFloat(AggregateCombiner<?> combiner) {
  return combiner.isNull() ? null : combiner.getFloat();
}

Try / catch

try {
  float v = combiner.getFloat();
} catch (IllegalStateException e) {
  if (e.getMessage() != null && e.getMessage().contains("Null Value")) {
    // treat as SQL NULL
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/1c244a62d9e8c53a. Report an issue: GitHub.