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

  1. File a Vitess bug with the full query and vschema (the error is tagged [BUG])
  2. Rewrite the query without the CTE (inline the subquery/derived table) as a workaround
  3. Try simplifying the join predicate to direct column-to-column comparisons
  4. 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

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


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