apache/druid · error · AggregatorFactoryNotMergeableException

AggregatorFactoryNotMergeableException

Error message

AggregatorFactoryNotMergeableException

What it means

SimpleLongAggregatorFactory.getMergingFactory throws AggregatorFactoryNotMergeableException when the other factory has a different name or a different class. Native queries merge partial aggregation results from each segment; this only works if both factories are identical long-based aggregators with the same output name. The exception tells the planner it cannot produce a combined factory.

Source

Thrown at processing/src/main/java/org/apache/druid/query/aggregation/SimpleLongAggregatorFactory.java:186

  {
    return object;
  }

  @Override
  public List<String> requiredFields()
  {
    return fieldName != null
           ? Collections.singletonList(fieldName)
           : fieldExpression.get().analyzeInputs().getRequiredBindingsList();
  }

  @Override
  public AggregatorFactory getMergingFactory(AggregatorFactory other) throws AggregatorFactoryNotMergeableException
  {
    if (other.getName().equals(this.getName()) && this.getClass() == other.getClass()) {
      return getCombiningFactory();
    } else {
      throw new AggregatorFactoryNotMergeableException(this, other);
    }
  }

  @Override
  public int hashCode()
  {
    return Objects.hash(fieldName, expression, name);
  }

  @Override
  public boolean equals(Object o)
  {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Ensure both sides use the same output name for the long aggregator
  2. Use identical long aggregator classes (longSum/longMin/longMax/longFirst/longAny) on both sides
  3. Rewrite the query to align inner and outer aggregations, or let the subquery perform its own final aggregation (use finalizeOuterAggregate=true where appropriate)
  4. Re-ingest or normalize schema if historical segments use a different metric type under the same name

Example fix

// before
 inner:  longSum s ; outer: doubleSum s
// after
 inner:  longSum s ; outer: longSum s (or rename the outer metric)
Defensive patterns

Strategy: validation

Validate before calling

boolean mergeable(AggregatorFactory a, AggregatorFactory b) {
  return a.getName().equals(b.getName()) && a.getClass() == b.getClass();
}

Type guard

boolean isMergeableLongPair(AggregatorFactory other, SimpleLongAggregatorFactory self) {
  return other instanceof SimpleLongAggregatorFactory && other.getName().equals(self.getName());
}

Try / catch

try {
  AggregatorFactory merged = factory.getMergingFactory(other);
} catch (AggregatorFactoryNotMergeableException e) {
  LOGGER.warn(e, "Non-mergeable long aggregators: %s vs %s", e.getFactory1(), e.getFactory2());
}

Prevention

When it happens

Trigger: Query fan-out across segments/subqueries where the same output name maps to a SimpleLongAggregatorFactory on one side and a different aggregator class or name on the other, causing merge code to call getMergingFactory.

Common situations: Subquery/outer query with mismatched long aggregation names; ingestion specs changed a metric from long to float/double over time; inline datasources (JOIN results) that don't carry the same aggregator setup as base segments.

Related errors


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