apache/druid · error · java.lang.UnsupportedOperationException

StringFirstAggregator does not support getFloat()

Error message

StringFirstAggregator does not support getFloat()

What it means

StringFirstAggregator computes the earliest string value per group; its result type is a string (wrapped in a SerializablePairLongString), not numeric. getFloat() is therefore unsupported and always throws UnsupportedOperationException. Queries or post-aggregations requesting a float from this aggregator are invalid.

Source

Thrown at processing/src/main/java/org/apache/druid/query/aggregation/firstlast/first/StringFirstAggregator.java:95

      if (time < firstTime) {
        final String value = DimensionHandlerUtils.convertObjectToString(valueSelector.getObject());
        firstTime = time;
        firstValue = StringUtils.fastLooseChop(value, maxStringBytes);
      }
    }
  }

  @Override
  public Object get()
  {
    return new SerializablePairLongString(firstTime, StringUtils.chop(firstValue, maxStringBytes));
  }

  @Override
  public float getFloat()
  {
    throw new UnsupportedOperationException("StringFirstAggregator does not support getFloat()");
  }

  @Override
  public long getLong()
  {
    throw new UnsupportedOperationException("StringFirstAggregator does not support getLong()");
  }

  @Override
  public double getDouble()
  {
    throw new UnsupportedOperationException("StringFirstAggregator does not support getDouble()");
  }

  @Override
  public void close()
  {
    // no resources to cleanup

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Do not call getFloat(); use the aggregator's string result via the proper getter/serialized form.
  2. Remove any post-aggregator or outputType that treats stringFirst output as numeric.
  3. Use a numeric-first/last aggregator (e.g. FloatFirst/DoubleFirst) if a numeric earliest value is needed.
  4. Wrap the access with a type check on the aggregator's result type before invoking numeric getters.

Example fix

// before
AggregatorFactory first = new StringFirstAggregatorFactory("f", "col", null, null);
float v = ((BufferAggregator) agg).getFloat(buf, pos);
// after
Object v = ((StringFirstBufferAggregator) agg).get(buf, pos); // string pair, not float
Defensive patterns

Strategy: type-guard

Validate before calling

if (aggregator instanceof StringFirstAggregator) {
  Object v = aggregator.get(); // string pair
} else {
  float v = aggregator.getFloat();
}

Type guard

boolean hasNumericResult(Aggregator a) {
  return !(a instanceof StringFirstAggregator);
}

Try / catch

try {
  return agg.getFloat();
} catch (UnsupportedOperationException e) {
  Object pair = agg.get();
  return pair == null ? 0f : Float.parseFloat(String.valueOf(((SerializablePairLongString) pair).rhs));
}

Prevention

When it happens

Trigger: Accessing getFloat() on a StringFirstAggregator result — typically a finalizing/post-aggregation step treating the string-first output as a float, or a factory contract calling numeric getters unconditionally.

Common situations: Writing a custom post-aggregator over stringFirst output, using stringFirst with an outputType of float/long, or framework code iterating numeric getters on an aggregator buffer.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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