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
- File the issue with the stack trace and plan — this is an internal invariant violation, not user error
- Upgrade Presto to a version with the canonical-plan stats fix
- 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
- Do not invoke assignStatsEquivalentPlanNode on CanonicalTableScanNode
- Dereference to the underlying table scan for stats mapping
- Report stack traces upstream — user code should never reach this path
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
- GENERIC_INTERNAL_ERROR
- GENERIC_INTERNAL_ERROR
- GENERIC_INTERNAL_ERROR
- GENERIC_INTERNAL_ERROR
- GENERIC_INTERNAL_ERROR
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/672da95920bd89cc.
Report an issue: GitHub.