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

  1. 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
  2. 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
  3. 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

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.