apache/druid · error · AggregatorFactoryNotMergeableException

AggregatorFactoryNotMergeableException

Error message

AggregatorFactoryNotMergeableException

What it means

Druid throws AggregatorFactoryNotMergeableException from JavaScriptAggregatorFactory.getMergingFactory when two aggregators from different query sub-results cannot be combined into a single merge step. For a merge to be allowed, both factories must have the same name and the same class (JavaScriptAggregatorFactory), and their fnCombine and fnReset JavaScript functions must be string-identical. If any of these checks fails, the engine cannot safely merge partial aggregation results, so it aborts with this exception.

Source

Thrown at processing/src/main/java/org/apache/druid/query/aggregation/JavaScriptAggregatorFactory.java:174

    };
  }

  @Override
  public AggregatorFactory getCombiningFactory()
  {
    return new JavaScriptAggregatorFactory(name, Collections.singletonList(name), fnCombine, fnReset, fnCombine, config);
  }

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

  @Override
  public Object deserialize(Object object)
  {
    // handle "NaN" / "Infinity" values serialized as strings in JSON
    if (object instanceof String) {
      return Double.parseDouble((String) object);
    }
    return object;
  }

  @Nullable
  @Override
  public Object finalizeComputation(@Nullable Object object)
  {
    return object;
  }

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Ensure every query layer (ingestion spec, query spec, sub-specs) defines the exact same fnCombine and fnReset JavaScript code for aggregators sharing a name.
  2. Verify all aggregators being merged at the same level are the same type and registered under the same unique name.
  3. Rewrite the aggregation to avoid merging incompatible specs (e.g. split into two queries and combine in application code, or use a post-aggregator instead).
  4. As a last resort, replace the JavaScript aggregator with a native Druid aggregator (e.g. doubleSum) whose merge semantics are uniform across specs.

Example fix

// before: two specs with same name, different combine scripts
// spec A: {"type":"javascript","name":"agg","fieldNames":["x"],"fnAggregate":...,"fnCombine":"function(a,b){return a+b;}","fnReset":"function(){return 0;}"}
// spec B: {"type":"javascript","name":"agg", ..., "fnCombine":"function(a,b){return Math.max(a,b);}"}
// after: identical fnCombine/fnReset in every spec that shares the aggregator name
// spec B: {"type":"javascript","name":"agg", ..., "fnCombine":"function(a,b){return a+b;}","fnReset":"function(){return 0;}"}
Defensive patterns

Strategy: validation

Validate before calling

// before merging sub-aggregation specs, validate mergeability
static boolean mergeable(AggregatorFactory a, AggregatorFactory b) {
  if (!(a instanceof JavaScriptAggregatorFactory) || !(b instanceof JavaScriptAggregatorFactory)) return false;
  JavaScriptAggregatorFactory x = (JavaScriptAggregatorFactory) a, y = (JavaScriptAggregatorFactory) b;
  return x.getName().equals(y.getName())
      && x.getFnCombine().equals(y.getFnCombine())
      && x.getFnReset().equals(y.getFnReset());
}

Type guard

if (!(other instanceof JavaScriptAggregatorFactory)) {
  // cannot merge; handle as incompatible aggregator type
}

Try / catch

try {
  AggregatorFactory merged = factory.getMergingFactory(other);
} catch (AggregatorFactoryNotMergeableException e) {
  log.error(e, "Aggregators '%s' and '%s' cannot be merged; check name/type/fnCombine/fnReset", e.getThisFactory(), e.getOtherFactory());
  // fail the query with a clear config error instead of retrying
}

Prevention

When it happens

Trigger: Calling getMergingFactory(other) where (1) other's aggregator name differs from this factory's name, (2) other is a different AggregatorFactory subclass (e.g. LongSumAggregatorFactory paired with JavaScriptAggregatorFactory of the same name), or (3) both are JavaScriptAggregatorFactory with the same name but their fnCombine or fnReset scripts differ (string inequality). This fires during distributed query processing when the broker merges partial results from multiple segments or historical nodes.

Common situations: A datasource ingested with one JavaScript aggregator definition is queried with a modified script (fnCombine changed between versions); two aggregation specs in a multi-stage/merge query use the same output name but different JavaScript combine logic; a typo makes the aggregator name differ from the post-aggregation reference; mixing a native JavaScript aggregator with a SQL-generated equivalent aggregator on the same column.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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