apache/druid · error · IllegalStateException
There can only be one aggregation for column
Error message
There can only be one aggregation for column [%s].
What it means
Raised in SegmentGenerationUtils.makeDimensionsAndAggregatorsForIngestion when, for a rollup MSQ ingestion, an output column ends up with more than one AggregatorFactory. Each rollup output column must be produced by exactly one aggregator; duplicates indicate a corrupted columnMappings/aggregator mapping built from the query plan.
Solutions
- Check that the ingestion SQL does not alias the same aggregated expression to a column name that is aggregated again.
- If this arises from an internally generated plan, file a bug with the query: the columnMappings → aggregator assignment should never produce duplicates.
- Work around by renaming conflicting output columns in the SQL.
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at multi-stage-query/src/main/java/org/apache/druid/msq/indexing/destination/SegmentGenerationUtils.java:363 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/b3a9490e1491b545.
Report an issue: GitHub.
Appendix: source
Thrown at multi-stage-query/src/main/java/org/apache/druid/msq/indexing/destination/SegmentGenerationUtils.java:363
for (final KeyColumn clusterByColumn : queryClusterBy.getColumns()) {
final IntList outputColumns = columnMappings.getOutputColumnsForQueryColumn(clusterByColumn.columnName());
for (final int outputColumn : outputColumns) {
outputColumnsInOrder.add(columnMappings.getOutputColumnName(outputColumn));
}
}
// Then all other columns.
outputColumnsInOrder.addAll(columnMappings.getOutputColumnNames());
Map<String, AggregatorFactory> outputColumnAggregatorFactories = new HashMap<>();
if (isRollupQuery) {
// Populate aggregators from the native query when doing an ingest in rollup mode.
for (AggregatorFactory aggregatorFactory : ((GroupByQuery) query).getAggregatorSpecs()) {
for (final int outputColumn : columnMappings.getOutputColumnsForQueryColumn(aggregatorFactory.getName())) {
final String outputColumnName = columnMappings.getOutputColumnName(outputColumn);
if (outputColumnAggregatorFactories.containsKey(outputColumnName)) {
throw new ISE("There can only be one aggregation for column [%s].", outputColumn);
} else {
outputColumnAggregatorFactories.put(
outputColumnName,
aggregatorFactory.withName(outputColumnName).getCombiningFactory()
);
}
}
}
}
// Each column can be either a dimension or an aggregator.
// For non-complex columns, If the aggregator factory of the column is not available, we treat the column as
// a dimension. For complex columns, certains hacks are in place.
for (final String outputColumnName : outputColumnsInOrder) {
// CollectionUtils.getOnlyElement because this method is only called during ingestion, where we require
// that output names be unique.
final int outputColumn = CollectionUtils.getOnlyElement(
columnMappings.getOutputColumnsByName(outputColumnName),View on GitHub (pinned to 9b90983fd2)