vitessio/vitess · error · VitessError
VT13001
VT13001
Error message
VT13001: [BUG] pushed wrong predicate to the join: %s
What it means
VT13001 is Vitess's internal-bug panic code. This one is raised in AddPredicate on a join operator when a predicate could not be classified as belonging to the left side, right side, or as a join predicate — i.e. the predicate-pushdown logic hit an expression shape it was not designed to handle. It signals a planner bug or an unsupported query construct, not a user configuration problem.
Source
Thrown at go/vt/vtgate/planbuilder/operators/joins.go:90
case deps.IsSolvedBy(TableID(join)):
// if we are dealing with an outer join, always start by checking if this predicate can turn
// the join into an inner join
if !joinPredicates && IsOuter(join) && canConvertToInner(ctx, expr, TableID(join.GetRHS())) {
join.MakeInner()
}
if !joinPredicates && IsOuter(join) {
// if we still are dealing with an outer join
// we need to filter after the join has been evaluated
return newFilter(join, expr)
}
join.AddJoinPredicate(ctx, expr, true)
return join
}
panic(vterrors.VT13001("pushed wrong predicate to the join: " + sqlparser.String(expr)))
}
// we are looking for predicates like `tbl.col = <>` or `<> = tbl.col`,
// where tbl is on the rhs of the left outer join
// When a predicate uses information from an outer table, we can convert from an outer join to an inner join
// if the predicate is "null-intolerant".
//
// Null-intolerant in this context means that the predicate will not be true if the table columns are null.
//
// Since an outer join is an inner join with the addition of all the rows from the left-hand side that
// matched no rows on the right-hand, if we are later going to remove all the rows where the right-hand
// side did not match, we might as well turn the join into an inner join.
//
// This is based on the paper "Canonical Abstraction for Outerjoin Optimization" by J Rao et al.
func canConvertToInner(ctx *plancontext.PlanningContext, expr sqlparser.Expr, rhs semantics.TableSet) bool {
isColNameFromRHS := func(e sqlparser.Expr) bool {
return sqlparser.IsColName(e) && ctx.SemTable.RecursiveDeps(e).IsSolvedBy(rhs)
}View on GitHub (pinned to 01a25a7d17)
Solutions
- Reduce the query to the minimal failing statement and file a bug at github.com/vitessio/vitess with the SQL, VSchema, and full stack trace
- Rewrite the query so the predicate is a simple column equality usable as a join predicate or clearly bound to one side of the join
- Test on the latest Vitess release; this path has been actively replaced by AST rewriters in newer versions
Example fix
// before: predicate shape the join cannot classify ... WHERE a.x + b.y = 5 ... // after: rewrite as a predicate each side/planner understands ... WHERE a.x = 5 AND b.y = 5 ...
Defensive patterns
Strategy: fallback
Validate before calling
// pre-check in app layer: keep join predicates simple column equalities
if (!/^\w+\.\w+\s*=\s*\w+\.\w+$|^[\w.]+\s*=\s*\?/.test(whereClause)) {
console.warn('complex join predicate may hit VT13001');
} Type guard
function isSimpleEquality(expr) {
return expr != null && expr.type === 'binary' &&
expr.operator === '=' &&
expr.operands.every(o => o.type === 'column' || o.type === 'value');
} Try / catch
try {
await vtgate.execute(session, query, bindVars);
} catch (e) {
if (String(e.message).includes('VT13001')) {
// internal planner bug: log SQL + stack, fall back to simplified query, report upstream
}
throw e;
} Prevention
- Keep join predicates as simple column equalities
- Avoid exotic predicates (functions over multiple joined tables) in WHERE/ON
- Pin and test the Vitess version before planner-heavy query rollouts
- Report any VT13001 to Vitess with minimal repro
When it happens
Trigger: Calling AddPredicate on a *Join operator with a predicate expression that is neither pushable to the LHS, pushable to the RHS, nor splittable as a join predicate (e.g. predicates referencing tables in ways the LHS/RHS column sets don't recognize). Reached via the planner's predicate-pushdown phase while planning a query with joins.
Common situations: Running an unusual SQL query (odd ON-clause or WHERE predicate over a join, correlated references, mixed keyspace joins) through vtgate; a query that regressed after a Vitess upgrade because new predicate shapes are routed into this code path.
Related errors
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/5dccffdb42b4d008.
Report an issue: GitHub.