vitessio/vitess · error · VitessError
VT13001
VT13001
Error message
VT13001: [BUG] expected column name
What it means
VT13001 is an internal BUG error raised in breakCTEExpressionInLhsAndRhs in go/vt/vtgate/planbuilder/operators/join.go:168 when splitting a predicate between LHS and RHS of a CTE-containing join: a BindVarExpr on the LHS side holds something other than a *sqlparser.ColName. This should be unreachable for valid planner inputs, so it is reported as a [BUG] rather than a user error.
Source
Thrown at go/vt/vtgate/planbuilder/operators/join.go:168
pred sqlparser.Expr,
cte *plancontext.ContextCTE,
) sqlparser.Expr {
expr := breakCTEExpressionInLhsAndRhs(ctx, pred, cte.Id)
predicate := ctx.PredTracker.NewJoinPredicate(expr.RightExpr)
expr.JoinPredicateID = &predicate.ID
expr.RightExpr = predicate
cte.Predicates = append(cte.Predicates, expr)
return predicate
}
func breakCTEExpressionInLhsAndRhs(ctx *plancontext.PlanningContext, pred sqlparser.Expr, lhsID semantics.TableSet) *plancontext.RecurseExpression {
col := breakExpressionInLHSandRHS(ctx, pred, lhsID)
lhsExprs := slice.Map(col.LHSExprs, func(bve BindVarExpr) plancontext.BindVarExpr {
col, ok := bve.Expr.(*sqlparser.ColName)
if !ok {
panic(vterrors.VT13001("expected column name"))
}
return plancontext.BindVarExpr{
Name: bve.Name,
Expr: col,
}
})
return &plancontext.RecurseExpression{
Original: col.Original,
RightExpr: col.RHSExpr,
LeftExprs: lhsExprs,
}
}
func createJoin(ctx *plancontext.PlanningContext, LHS, RHS Operator) Operator {
lqg, lok := LHS.(*QueryGraph)
rqg, rok := RHS.(*QueryGraph)
if lok && rok {
op := &QueryGraph{View on GitHub (pinned to 01a25a7d17)
Solutions
- File a Vitess bug with the full query and vschema (the error is tagged [BUG])
- Rewrite the query without the CTE (inline the subquery/derived table) as a workaround
- Try simplifying the join predicate to direct column-to-column comparisons
- Check whether a newer Vitess version has fixed the planner issue and upgrade
Example fix
// before (workaround) WITH c AS (SELECT id FROM t) SELECT * FROM c JOIN u ON u.id = c.id AND <complex expr>; // after SELECT * FROM (SELECT id FROM t) AS c JOIN u ON u.id = c.id;
Defensive patterns
Strategy: try-catch
Validate before calling
// No reliable user-side check: this is a planner-internal invariant failure. // Validate the query parses and keep a simplified fallback available. const isCteJoinQuery = /\bWITH\b[\s\S]*\bJOIN\b/i.test(sql);
Try / catch
try {
return await vtgate.execute(sql, args);
} catch (e) {
if (String(e).includes('VT13001')) {
reportBug('vitess planner', { query: sql, error: e.message }); // [BUG] — file an issue
return await vtgate.execute(rewriteWithoutCte(sql), args); // fallback
}
throw e;
} Prevention
- Keep CTE join predicates as direct column comparisons
- Pin and track Vitess planner fixes; upgrade when [BUG] errors appear
- Always report VT13001 occurrences with query and vschema — it signals a planner defect
When it happens
Trigger: Planner-internal condition: breakExpressionInLHSandRHS returns an LHS expression list containing a non-column expression while splitting a predicate across a CTE boundary in join planning.
Common situations: Encountered with unusual CTE + join predicate combinations; typically a planner bug report rather than a user misconfiguration — often seen after upgrading Vitess with queries mixing CTEs and lateral/complex predicates.
Related errors
- cannot update immutable column
- unsupported query construct
- attempted to execute unprepared query
- IntervalDateExpr.Unit is not set
- [BUG] tried to replace 'SQLNode' on 'RootNode'
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/6a29ded3a36d1154.
Report an issue: GitHub.