prestodb/presto · error · PrestoException

GENERIC_INTERNAL_ERROR

GENERIC_INTERNAL_ERROR

Error message

Cannot assign canonical plan id to Canonical table scan node: %s

What it means

Like CanonicalJoinNode, a CanonicalTableScanNode cannot be assigned a stats-equivalent plan node id; doing so breaks canonical-planner invariants and throws GENERIC_INTERNAL_ERROR. Canonical table scans are placeholders, so stats-equivalence bookkeeping must target the real node.

Source

Thrown at presto-main-base/src/main/java/com/facebook/presto/sql/planner/CanonicalTableScanNode.java:102

    @Override
    @JsonProperty
    public List<VariableReferenceExpression> getOutputVariables()
    {
        return outputVariables;
    }

    @Override
    public PlanNode replaceChildren(List<PlanNode> newChildren)
    {
        checkArgument(newChildren.isEmpty(), "newChildren is not empty");
        return this;
    }

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

    @JsonProperty
    public CanonicalTableHandle getTable()
    {
        return table;
    }

    @JsonProperty
    public Map<VariableReferenceExpression, ColumnHandle> getAssignments()
    {
        return assignments;
    }

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

View on GitHub (pinned to 55bb57d202)

Solutions

  1. File the issue with the stack trace and plan — this is an internal invariant violation, not user error
  2. Upgrade Presto to a version with the canonical-plan stats fix
  3. In custom code, dereference the wrapped source node before recording stats equivalence

Example fix

// before
canonicalTableScan.assignStatsEquivalentPlanNode(Optional.of(equivalentNode));
// after
PlanNode target = canonicalTableScan.getSource(); // real scan node
target.assignStatsEquivalentPlanNode(Optional.of(equivalentNode));
Defensive patterns

Strategy: try-catch

Validate before calling

if (node instanceof CanonicalTableScanNode) {
    throw new IllegalStateException("Assign stats-equivalent id to the wrapped scan node, not the canonical wrapper");
}

Type guard

boolean isCanonicalWrapper(PlanNode n) {
    return n instanceof CanonicalTableScanNode || n instanceof CanonicalJoinNode;
}

Try / catch

try { planner.plan(...); } catch (PrestoException e) { if (e.getErrorCode().equals(GENERIC_INTERNAL_ERROR.toErrorCode()) && e.getMessage().contains("Canonical table scan node")) { reportInternalBug(e); } else throw e; }

Prevention

When it happens

Trigger: Internal planner flow where assignStatsEquivalentPlanNode(Optional.of(node)) is called on a CanonicalTableScanNode during canonical plan generation or stats extraction.

Common situations: Engine bugs with canonical plan stats collection (e.g. EXPLAIN with stats, cost-based optimization paths); custom rules or extensions mishandling canonical wrappers; recent version upgrades exposing new plan shapes.

Related errors


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