vitessio/vitess · error

VT13001

VT13001

Error message

did not expect this method to be called

What it means

VT13001 signals an internal planner programming error: a method was invoked on an operator that explicitly does not support it. Here, Table.AddColumn was called, but a Table operator derives its columns directly from the underlying table and never accepts columns added through the operator-column API; such additions are only valid on derived/derived-table-like operators. The panic exists to catch planner bugs early, since silently ignoring the call would produce a broken plan.

Source

Thrown at go/vt/vtgate/planbuilder/operators/table.go:74

		Columns: columns,
	}
}

// Introduces implements the PhysicalOperator interface
func (to *Table) introducesTableID() semantics.TableSet {
	if to.QTable == nil {
		return semantics.EmptyTableSet()
	}
	return to.QTable.ID
}

// AddPredicate implements the PhysicalOperator interface
func (to *Table) AddPredicate(_ *plancontext.PlanningContext, expr sqlparser.Expr) Operator {
	return newFilter(to, expr)
}

func (to *Table) AddColumn(*plancontext.PlanningContext, bool, bool, *sqlparser.AliasedExpr) int {
	panic(vterrors.VT13001("did not expect this method to be called"))
}

func (*Table) AddWSColumn(*plancontext.PlanningContext, int, bool) int {
	panic(vterrors.VT13001("did not expect this method to be called"))
}

func (to *Table) FindCol(ctx *plancontext.PlanningContext, expr sqlparser.Expr, underRoute bool) int {
	colToFind, ok := expr.(*sqlparser.ColName)
	if !ok {
		return -1
	}

	for idx, colName := range to.Columns {
		if colName.Name.Equal(colToFind.Name) {
			return idx
		}
	}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Fix the caller so AddColumn is not invoked on a Table; column additions against a table should go through AddPredicate (which wraps it in a Filter) or the caller must materialize the column in an enclosing operator.
  2. Check which operator delegated the call (stack trace) and ensure derived/outer operators implement AddColumn themselves rather than forwarding to Table.
  3. If you control the query, rewrite it so the extra column is produced by a derived table or select expression instead of relying on planner pushdown.
  4. If it reproduces on a stock Vitess version with an ordinary query, file a bug with the query and stack trace — it is a planner defect.

Example fix

// before: pushing a column into a Table operator
idx := tableOp.AddColumn(ctx, false, false, aliasedExpr)

// after: express it as a predicate/filter or use the enclosing operator
newOp := tableOp.AddPredicate(ctx, aliasedExpr.Expr)
Defensive patterns

Strategy: validation

Validate before calling

_, isTable := op.(*operators.Table)
if isTable {
    // route the column through AddPredicate or an enclosing operator instead
    return
}

Type guard

func isColumnCapable(op operators.Operator) bool {
    switch op.(type) {
    case *operators.Table, *operators.Vindex:
        return false
    }
    return true
}

Try / catch

// planner panics are unrecoverable programming errors; do not try/catch.
// Guard before calling:
if t, ok := op.(*operators.Table); ok {
    return t.AddPredicate(ctx, aliasedExpr.Expr)
}
idx := op.AddColumn(ctx, _, _, aliasedExpr)

Prevention

When it happens

Trigger: Calling Table.AddColumn(ctx, _, _, *sqlparser.AliasedExpr) directly, or the planner's generic column-pushdown path reaching a Table operator because an outer operator (e.g. a join or derived table) delegated AddColumn to a source that is a plain Table instead of handling column materialization itself.

Common situations: Hitting this while extending the Vitess planbuilder (adding a new operator, join algorithm, or aggregation path) and routing column additions down to a Table source; also seen when porting queries that previously worked through older pushdown code paths to the newer operator-based planner.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/62e5e1bc37a2c945. Report an issue: GitHub.