apache/druid · error · java.lang.UnsupportedOperationException

StringFirstAggregator does not support getDouble()

Error message

StringFirstAggregator does not support getDouble()

What it means

Sentinel guard in the string-first aggregator's typed read API: this aggregator's result is a SerializablePairLongString (timestamp + string value), which has no double representation, so getDouble() is intentionally unsupported. It fires when a query plan binds a string-first first/last aggregation output to a DOUBLE-typed read; use getFloat()/getLong()-free object access (via the pair) or an aggregation whose output matches the requested primitive type.

Source

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

    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. Use the aggregator's object/string getter to read the value.
  2. Drop the DOUBLE outputType or arithmetic post-aggregator applied to stringFirst output.
  3. Use DoubleFirstAggregator (type "doubleFirst") when an earliest numeric double is needed.
  4. Guard access by checking the aggregator type before invoking getDouble().

Example fix

// before
double d = aggregator.getDouble();
// after
Object pair = aggregator.get(); // SerializablePairLongString
String s = pair == null ? null : ((SerializablePairLongString) pair).rhs;
Defensive patterns

Strategy: type-guard

Validate before calling

if (aggregator instanceof StringFirstAggregator) {
  Object v = aggregator.get();
} else {
  double v = aggregator.getDouble();
}

Type guard

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

Try / catch

try {
  return agg.getDouble();
} catch (UnsupportedOperationException e) {
  Object pair = agg.get();
  return pair == null ? 0d : Double.parseDouble(String.valueOf(((SerializablePairLongString) pair).rhs));
}

Prevention

When it happens

Trigger: Calling getDouble() on StringFirstAggregator, usually from a post-aggregator, finalizer, or generic numeric accessor treating the aggregate as a double.

Common situations: Using stringFirst inside arithmetic post-aggregations; configuring a DOUBLE outputType for stringFirst; shared aggregator-reading code paths that call all numeric getters.

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/cd8398edff90b19a. Report an issue: GitHub.