prestodb/presto · error · PrestoException

GENERIC_INTERNAL_ERROR

GENERIC_INTERNAL_ERROR

Error message

Unexpected call replaceChildren for %s

What it means

StatsEquivalentPlanNodeWithLimit is a plan-node wrapper used for statistics equivalence tracking that intentionally does not support structural rewrites. Its replaceChildren override unconditionally throws GENERIC_INTERNAL_ERROR, so calling it means the optimizer tried to modify children of a node that must remain immutable — an engine-internal invariant violation.

Source

Thrown at presto-main-base/src/main/java/com/facebook/presto/sql/planner/StatsEquivalentPlanNodeWithLimit.java:94

    }

    @JsonProperty
    public PlanNode getLimit()
    {
        return limit;
    }

    @Override
    public List<VariableReferenceExpression> getOutputVariables()
    {
        return plan.getOutputVariables();
    }

    @Override
    @JsonProperty
    public PlanNode replaceChildren(List<PlanNode> newChildren)
    {
        throw new PrestoException(GENERIC_INTERNAL_ERROR, format("Unexpected call replaceChildren for %s", this));
    }

    @Override
    @JsonProperty
    public PlanNode assignStatsEquivalentPlanNode(Optional<PlanNode> statsEquivalentPlanNode)
    {
        throw new PrestoException(GENERIC_INTERNAL_ERROR, format("Cannot assign canonical plan id for: %s", this));
    }

    @Override
    public <R, C> R accept(InternalPlanVisitor<R, C> visitor, C context)
    {
        return visitor.visitStatsEquivalentPlanNodeWithLimit(this, context);
    }

    @Override
    public boolean equals(Object o)
    {

View on GitHub (pinned to 55bb57d202)

Solutions

  1. This indicates an internal error — file a bug with the query and full stack trace to the Presto project
  2. Identify the optimizer rule in the stack trace that rewrote the node and exclude/bypass it
  3. Upgrade Presto to a version where the rewriting rule handles StatsEquivalentPlanNodeWithLimit correctly
  4. If you wrote a custom rule/visitor, skip or specially handle StatsEquivalentPlanNodeWithLimit nodes
Defensive patterns

Strategy: try-catch

Type guard

boolean isStatsEquivalentWrapper(PlanNode node) {
    return node instanceof StatsEquivalentPlanNodeWithLimit;
}

Try / catch

try {
    optimizer.apply(rules, plan);
} catch (PrestoException e) {
    if (e.getErrorCode().getName().equals("GENERIC_INTERNAL_ERROR") && e.getMessage().contains("replaceChildren")) {
        throw new IllegalStateException("Optimizer rule rewrote StatsEquivalentPlanNodeWithLimit; report bug", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: An optimizer rule (e.g. a PlanRewriter or node-replace pass) invokes replaceChildren on a StatsEquivalentPlanNodeWithLimit instance during plan optimization.

Common situations: A Presto engine bug where a plan optimizer rule does not skip stats-equivalence wrapper nodes; custom optimizer rules/plugins that traverse and rewrite all plan nodes without special-casing this type.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/0a22747c6fdfff92. Report an issue: GitHub.