{"record":{"id":"91f806c89afa3f39","repo":"prestodb/presto","slug":"generic-internal-error-91f806","errorCode":"GENERIC_INTERNAL_ERROR","errorMessage":"Unexpected plan node between partial and final aggregation","messagePattern":"Unexpected plan node between partial and final aggregation","errorType":"error_code","errorClass":"PrestoException","httpStatus":null,"severity":"error","filePath":"presto-main-base/src/main/java/com/facebook/presto/sql/planner/optimizations/MergePartialAggregationsWithFilter.java","lineNumber":201,"sourceCode":"\n        public static RowExpression ifThenElse(RowExpression... arguments)\n        {\n            return specialForm(SpecialFormExpression.Form.IF, arguments[1].getType(), arguments);\n        }\n\n        public boolean isPlanChanged()\n        {\n            return planChanged;\n        }\n\n        @Override\n        public PlanNode visitPlan(PlanNode node, RewriteContext<Context> context)\n        {\n            List<PlanNode> children = node.getSources().stream()\n                    .map(child -> context.rewrite(child, context.get()))\n                    .collect(toImmutableList());\n            if (!context.get().isEmpty()) {\n                throw new PrestoException(GENERIC_INTERNAL_ERROR, \"Unexpected plan node between partial and final aggregation\");\n            }\n            return replaceChildren(node, children);\n        }\n\n        @Override\n        public PlanNode visitAggregation(AggregationNode node, RewriteContext<Context> context)\n        {\n            PlanNode rewrittenSource = context.rewrite(node.getSource(), context.get());\n            // Before optimization, for aggregations with filter, input rows will be skipped if mask is false. However, after optimization, the partial\n            // aggregation output is projected to be NULL if mask is false. We need to have the function to not calledOnNullInput to ensure correctness.\n            // Applying optimizations on global aggregations will lead to exception at\n            // https://github.com/prestodb/presto/blob/dfbf21744ccd900d1a650571ffc35915db9b9f59/presto-main/src/main/java/com/facebook/presto/operator/HashAggregationOperator.java#L627\n            boolean canOptimize = !node.getGroupingKeys().isEmpty() && node.getAggregations().values().stream()\n                    .map(x -> functionAndTypeManager.getFunctionMetadata(x.getFunctionHandle())).noneMatch(x -> x.isCalledOnNullInput());\n            if (canOptimize) {\n                checkState(node.getAggregations().values().stream().noneMatch(x -> x.getFilter().isPresent()), \"All aggregation filters should already be rewritten to mask before this optimization\");\n                if (node.getStep().equals(PARTIAL)) {\n                    planChanged = true;","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-main-base/src/main/java/com/facebook/presto/sql/planner/optimizations/MergePartialAggregationsWithFilter.java#L183-L219","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    execute(sql);\n} catch (PrestoException e) {\n    if (\"GENERIC_INTERNAL_ERROR\".equals(e.getErrorCode().getName()) && e.getMessage().contains(\"Unexpected plan node between partial and final aggregation\")) {\n        // restructure query or report bug; retrying will not help\n        reportBug(sql, e);\n    } else throw e;\n}","preventionTips":["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"],"tags":["planner","optimizer","aggregation","internal-error"],"backgroundTag":"planner-internal-invariant-violation","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}