apache/druid · error · UnsupportedOperationException
Cannot change output name for AggregatorFactory
Error message
Cannot change output name for AggregatorFactory[%s].
What it means
AggregatorFactory.withName(newName) is a no-op hook in the base class that throws UOE; only aggregator factories that support renaming override it. Calling it on an unsupported factory means the requested output-name substitution cannot be performed.
Solutions
- Override withName(String) in your AggregatorFactory subclass to return a copy with the new output name
- Use an aggregator implementation that supports withName
- Restructure the query to avoid renaming this aggregator (e.g. alias at a different layer)
Example fix
// before
// base class behavior
throw new UOE("Cannot change output name for AggregatorFactory[%s].", getClass().getName());
// after
@Override
public AggregatorFactory withName(String newName) {
return new DoubleSumAggregatorFactory(newName, fieldName, expression);
} Defensive patterns
Strategy: type-guard
Validate before calling
// before renaming
Method m = factory.getClass().getMethod("withName", String.class);
if (m.getDeclaringClass().equals(AggregatorFactory.class)) {
throw new IllegalStateException("Factory does not support withName: " + factory.getClass());
} Type guard
boolean supportsRename(AggregatorFactory f) {
try { f.withName("probe"); return true; } catch (UnsupportedOperationException e) { return false; }
} Try / catch
try {
renamed = factory.withName(newName);
} catch (UnsupportedOperationException e) {
log.error("withName unsupported for %s", factory.getClass(), e);
throw e;
} Prevention
- Override withName in custom aggregator factories
- Test custom aggregators in nested groupBy / renaming scenarios
- Avoid relying on output-name rewriting for legacy aggregator types
When it happens
Trigger: Calling withName() on a concrete AggregatorFactory that does not override it; callers include testWithName, substituteCombiningFactory, and getCombiningFactory paths during query planning/rewriting.
Common situations: Custom aggregators lacking a withName override used in queries that require renaming (e.g. nested groupBy or post-aggregation rewriting); older aggregator implementations not updated for the renaming API.
Related errors
- Aggregator[ ] cannot vectorize
- ApproximateHistogramBufferAggregator does not support…
- ApproximateHistogramBufferAggregator does not support…
- ApproximateHistogramBufferAggregator does not support…
- ApproximateHistogramFoldingAggregator does not support…
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/adb019c028f74f29.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/query/aggregation/AggregatorFactory.java:367
{
return this;
}
/**
* Used in cases where we want to change the output name of the aggregator to something else. For eg: if we have
* a query `select a, sum(b) as total group by a from table` the aggregator returned from the native group by query is "a0" set in
* {@link org.apache.druid.sql.calcite.rel.DruidQuery#computeAggregations}. We can use withName("total") to set the output name
* of the aggregator to "total".
* <p>
* As all implementations of this interface method may not exist, callers of this method are advised to handle such a case.
*
* @param newName newName of the output for aggregator factory
* @return AggregatorFactory with the output name set as the input param.
*/
@SuppressWarnings("unused")
public AggregatorFactory withName(String newName)
{
throw new UOE("Cannot change output name for AggregatorFactory[%s].", this.getClass().getName());
}
/**
* Check to see if we can make a 'combining' factory of this aggregator that is suitable to process input from a
* selector of values produced by the other {@link AggregatorFactory} representing pre-aggregated data. Typically,
* this means that this and the other aggregator have the same inputs ({@link #requiredFields()}, and the same
* options for how the data was constructed into the intermediary type. If suitable, this method returns a
* 'combining' aggregator factory of this aggregator to use to process the pre-aggregated data which was produced by
* the other aggregator.
* <p>
* This method is used indirectly in service of checking if a
* {@link org.apache.druid.segment.projections.QueryableProjection} can be used instead of the base table during
* {@link org.apache.druid.segment.CursorFactory#makeCursorHolder(CursorBuildSpec)}, which checks if this
* aggregator can be substituted for its combining aggregator if and only if there exists a column that a cursor can
* read which was created by an aggregator that satisfies this method. In other words, this aggregator is the 'query'
* aggregator defined on the {@link CursorBuildSpec}, the argument to this method is the aggregator which created
* some column whose selectors are available to the cursor. If all aggregators on the {@link CursorBuildSpec} can be
* paired with aggregators from the underlying table in the cursor factory, thenView on GitHub (pinned to 9b90983fd2)