{"record":{"id":"cd8398edff90b19a","repo":"apache/druid","slug":"stringfirstaggregator-does-not-support-getdouble","errorCode":null,"errorMessage":"StringFirstAggregator does not support getDouble()","messagePattern":"StringFirstAggregator does not support getDouble\\(\\)","errorType":"exception","errorClass":"java.lang.UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/aggregation/firstlast/first/StringFirstAggregator.java","lineNumber":107,"sourceCode":"    return new SerializablePairLongString(firstTime, StringUtils.chop(firstValue, maxStringBytes));\n  }\n\n  @Override\n  public float getFloat()\n  {\n    throw new UnsupportedOperationException(\"StringFirstAggregator does not support getFloat()\");\n  }\n\n  @Override\n  public long getLong()\n  {\n    throw new UnsupportedOperationException(\"StringFirstAggregator does not support getLong()\");\n  }\n\n  @Override\n  public double getDouble()\n  {\n    throw new UnsupportedOperationException(\"StringFirstAggregator does not support getDouble()\");\n  }\n\n  @Override\n  public void close()\n  {\n    // no resources to cleanup\n  }\n}\n","sourceCodeStart":89,"sourceCodeEnd":116,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/aggregation/firstlast/first/StringFirstAggregator.java#L89-L116","documentation":"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.","triggerScenarios":"Calling getDouble() on StringFirstAggregator, usually from a post-aggregator, finalizer, or generic numeric accessor treating the aggregate as a double.","commonSituations":"Using stringFirst inside arithmetic post-aggregations; configuring a DOUBLE outputType for stringFirst; shared aggregator-reading code paths that call all numeric getters.","solutions":["Use the aggregator's object/string getter to read the value.","Drop the DOUBLE outputType or arithmetic post-aggregator applied to stringFirst output.","Use DoubleFirstAggregator (type \"doubleFirst\") when an earliest numeric double is needed.","Guard access by checking the aggregator type before invoking getDouble()."],"exampleFix":"// before\ndouble d = aggregator.getDouble();\n// after\nObject pair = aggregator.get(); // SerializablePairLongString\nString s = pair == null ? null : ((SerializablePairLongString) pair).rhs;","handlingStrategy":"type-guard","validationCode":"if (aggregator instanceof StringFirstAggregator) {\n  Object v = aggregator.get();\n} else {\n  double v = aggregator.getDouble();\n}","typeGuard":"boolean supportsDouble(Aggregator a) {\n  return !(a instanceof StringFirstAggregator);\n}","tryCatchPattern":"try {\n  return agg.getDouble();\n} catch (UnsupportedOperationException e) {\n  Object pair = agg.get();\n  return pair == null ? 0d : Double.parseDouble(String.valueOf(((SerializablePairLongString) pair).rhs));\n}","preventionTips":["Use doubleFirst instead of stringFirst when doubles are expected.","Avoid arithmetic post-aggregators directly over stringFirst output.","Check aggregator types before numeric extraction."],"tags":["druid","aggregation","unsupported-getter"],"backgroundTag":"unsupported-operation","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"}