{"record":{"id":"d15dbbbb59cc5f46","repo":"apache/druid","slug":"aggregatorfactorynotmergeableexception-d15dbb","errorCode":null,"errorMessage":"AggregatorFactoryNotMergeableException","messagePattern":"AggregatorFactoryNotMergeableException","errorType":"exception","errorClass":"AggregatorFactoryNotMergeableException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/aggregation/JavaScriptAggregatorFactory.java","lineNumber":174,"sourceCode":"    };\n  }\n\n  @Override\n  public AggregatorFactory getCombiningFactory()\n  {\n    return new JavaScriptAggregatorFactory(name, Collections.singletonList(name), fnCombine, fnReset, fnCombine, config);\n  }\n\n  @Override\n  public AggregatorFactory getMergingFactory(AggregatorFactory other) throws AggregatorFactoryNotMergeableException\n  {\n    if (other.getName().equals(this.getName()) && other.getClass() == this.getClass()) {\n      JavaScriptAggregatorFactory castedOther = (JavaScriptAggregatorFactory) other;\n      if (this.fnCombine.equals(castedOther.fnCombine) && this.fnReset.equals(castedOther.fnReset)) {\n        return getCombiningFactory();\n      }\n    }\n    throw new AggregatorFactoryNotMergeableException(this, other);\n  }\n\n  @Override\n  public Object deserialize(Object object)\n  {\n    // handle \"NaN\" / \"Infinity\" values serialized as strings in JSON\n    if (object instanceof String) {\n      return Double.parseDouble((String) object);\n    }\n    return object;\n  }\n\n  @Nullable\n  @Override\n  public Object finalizeComputation(@Nullable Object object)\n  {\n    return object;\n  }","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/aggregation/JavaScriptAggregatorFactory.java#L156-L192","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure every query layer (ingestion spec, query spec, sub-specs) defines the exact same fnCombine and fnReset JavaScript code for aggregators sharing a name.","Verify all aggregators being merged at the same level are the same type and registered under the same unique name.","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).","As a last resort, replace the JavaScript aggregator with a native Druid aggregator (e.g. doubleSum) whose merge semantics are uniform across specs."],"exampleFix":"// before: two specs with same name, different combine scripts\n// spec A: {\"type\":\"javascript\",\"name\":\"agg\",\"fieldNames\":[\"x\"],\"fnAggregate\":...,\"fnCombine\":\"function(a,b){return a+b;}\",\"fnReset\":\"function(){return 0;}\"}\n// spec B: {\"type\":\"javascript\",\"name\":\"agg\", ..., \"fnCombine\":\"function(a,b){return Math.max(a,b);}\"}\n// after: identical fnCombine/fnReset in every spec that shares the aggregator name\n// spec B: {\"type\":\"javascript\",\"name\":\"agg\", ..., \"fnCombine\":\"function(a,b){return a+b;}\",\"fnReset\":\"function(){return 0;}\"}","handlingStrategy":"validation","validationCode":"// before merging sub-aggregation specs, validate mergeability\nstatic boolean mergeable(AggregatorFactory a, AggregatorFactory b) {\n  if (!(a instanceof JavaScriptAggregatorFactory) || !(b instanceof JavaScriptAggregatorFactory)) return false;\n  JavaScriptAggregatorFactory x = (JavaScriptAggregatorFactory) a, y = (JavaScriptAggregatorFactory) b;\n  return x.getName().equals(y.getName())\n      && x.getFnCombine().equals(y.getFnCombine())\n      && x.getFnReset().equals(y.getFnReset());\n}","typeGuard":"if (!(other instanceof JavaScriptAggregatorFactory)) {\n  // cannot merge; handle as incompatible aggregator type\n}","tryCatchPattern":"try {\n  AggregatorFactory merged = factory.getMergingFactory(other);\n} catch (AggregatorFactoryNotMergeableException e) {\n  log.error(e, \"Aggregators '%s' and '%s' cannot be merged; check name/type/fnCombine/fnReset\", e.getThisFactory(), e.getOtherFactory());\n  // fail the query with a clear config error instead of retrying\n}","preventionTips":["Keep JavaScript aggregator definitions (fnAggregate/fnCombine/fnReset) in one shared source of truth reused by every ingestion and query spec.","Give each aggregator a unique name and never reuse a name for a different script or aggregator type.","Review JS aggregator scripts in code review like regular code; changes to fnCombine must be rolled out to all specs simultaneously.","Prefer native Druid aggregators over JavaScript ones so merge semantics are guaranteed by the engine."],"tags":["druid","aggregation","javascript","query-processing","merge"],"backgroundTag":"incompatible-source-type","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}