prestodb/presto · error · PrestoException
GENERIC_INTERNAL_ERROR
GENERIC_INTERNAL_ERROR
Error message
Unexpected plan node between partial and final aggregation
What it means
MergePartialAggregationsWithFilter rewrites plans where a partial aggregation is directly chained to its final aggregation so the filter can be merged. Its default visitPlan asserts no other node type appears between the partial and final aggregation; if the context still holds entries after rewriting all children, the optimizer found an unexpected plan node between them, indicating a broken planner invariant rather than a user mistake.
Source
Thrown at presto-main-base/src/main/java/com/facebook/presto/sql/planner/optimizations/MergePartialAggregationsWithFilter.java:201
public static RowExpression ifThenElse(RowExpression... arguments)
{
return specialForm(SpecialFormExpression.Form.IF, arguments[1].getType(), arguments);
}
public boolean isPlanChanged()
{
return planChanged;
}
@Override
public PlanNode visitPlan(PlanNode node, RewriteContext<Context> context)
{
List<PlanNode> children = node.getSources().stream()
.map(child -> context.rewrite(child, context.get()))
.collect(toImmutableList());
if (!context.get().isEmpty()) {
throw new PrestoException(GENERIC_INTERNAL_ERROR, "Unexpected plan node between partial and final aggregation");
}
return replaceChildren(node, children);
}
@Override
public PlanNode visitAggregation(AggregationNode node, RewriteContext<Context> context)
{
PlanNode rewrittenSource = context.rewrite(node.getSource(), context.get());
// Before optimization, for aggregations with filter, input rows will be skipped if mask is false. However, after optimization, the partial
// aggregation output is projected to be NULL if mask is false. We need to have the function to not calledOnNullInput to ensure correctness.
// Applying optimizations on global aggregations will lead to exception at
// https://github.com/prestodb/presto/blob/dfbf21744ccd900d1a650571ffc35915db9b9f59/presto-main/src/main/java/com/facebook/presto/operator/HashAggregationOperator.java#L627
boolean canOptimize = !node.getGroupingKeys().isEmpty() && node.getAggregations().values().stream()
.map(x -> functionAndTypeManager.getFunctionMetadata(x.getFunctionHandle())).noneMatch(x -> x.isCalledOnNullInput());
if (canOptimize) {
checkState(node.getAggregations().values().stream().noneMatch(x -> x.getFilter().isPresent()), "All aggregation filters should already be rewritten to mask before this optimization");
if (node.getStep().equals(PARTIAL)) {
planChanged = true;View on GitHub (pinned to 55bb57d202)
Solutions
- File a bug with the full query text and EXPLAIN (the failing SQL) so the planner team can extend the rewriter.
- Try restructuring the query (simplify the aggregation/filter composition, add explicit subqueries) to sidestep the pattern.
- Bisect or downgrade if the failure appeared after a Presto upgrade, and check release notes for related optimizer fixes.
Defensive patterns
Strategy: try-catch
Try / catch
try {
execute(sql);
} catch (PrestoException e) {
if ("GENERIC_INTERNAL_ERROR".equals(e.getErrorCode().getName()) && e.getMessage().contains("Unexpected plan node between partial and final aggregation")) {
// restructure query or report bug; retrying will not help
reportBug(sql, e);
} else throw e;
} Prevention
- Treat as a planner bug: capture EXPLAIN and the SQL for a bug report
- Simplify aggregation/filter compositions if a workaround is urgent
- Check release notes when the failure appears after an upgrade
When it happens
Trigger: A plan in which the optimizer's pattern matching identified a partial/final aggregation pair, but when rewriting descendants an intermediate node type (not handled by the specialized visitors) appears between partial and final aggregation — a planner bug or a rule-interaction edge case in query plan shape.
Common situations: Complex queries combining aggregations with filters, joins, or other optimization passes that interleave nodes between partial and final aggregation; usually surfaces after upgrading or with unusual query constructs; it is an internal invariant violation, not caused by user SQL semantics alone.
Related errors
- GENERIC_INTERNAL_ERROR
- CLICKHOUSE_QUERY_GENERATOR_FAILURE
- GENERIC_INTERNAL_ERROR
- FUNCTION_IMPLEMENTATION_ERROR
- GENERIC_INTERNAL_ERROR
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/91f806c89afa3f39.
Report an issue: GitHub.