vitessio/vitess · error

VT09018

VT09018

Error message

 (where clause missing)

What it means

VT09018 is raised by the Vindex operator's CheckValid when the routed table has no predicates. Vindex-based routing requires a WHERE condition (typically on the vindex column) to resolve shards; planning a SELECT/UPDATE/DELETE on the table without any WHERE clause makes the plan invalid. The message combines a shared 'wrong where condition' text with '(where clause missing)'.

Source

Thrown at go/vt/vtgate/planbuilder/operators/vindex.go:121

func (v *Vindex) GetSelectExprs(ctx *plancontext.PlanningContext) []sqlparser.SelectExpr {
	return transformColumnsToSelectExprs(ctx, v)
}

func (v *Vindex) GetOrdering(*plancontext.PlanningContext) []OrderBy {
	return nil
}

func (v *Vindex) GetColNames() []*sqlparser.ColName {
	return v.Columns
}

func (v *Vindex) AddCol(col *sqlparser.ColName) {
	v.Columns = append(v.Columns, col)
}

func (v *Vindex) CheckValid() {
	if len(v.Table.Predicates) == 0 {
		panic(vterrors.VT09018(wrongWhereCond + " (where clause missing)"))
	}
}

func (v *Vindex) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator {
	for _, e := range sqlparser.SplitAndExpression(nil, expr) {
		deps := ctx.SemTable.RecursiveDeps(e)
		if deps.NumberOfTables() > 1 {
			panic(vterrors.VT09018(wrongWhereCond + " (multiple tables involved)"))
		}
		// check if we already have a predicate
		if v.OpCode != engine.VindexNone {
			panic(vterrors.VT09018(wrongWhereCond + " (multiple filters)"))
		}

		// check LHS
		comparison, ok := e.(*sqlparser.ComparisonExpr)
		if !ok {
			panic(vterrors.VT09018(wrongWhereCond + " (not a comparison)"))

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Add a WHERE clause with an equality/IN predicate on the vindex column, e.g. WHERE id = 5.
  2. If a full scan is intended, do not use this vindex route (use a different routing path).
  3. If you believe the predicate exists, verify it is a single-table comparison on the 'id' column that AddPredicate accepts.

Example fix

// before
SELECT * FROM user
// after
SELECT * FROM user WHERE id = 5
Defensive patterns

Strategy: validation

Validate before calling

if len(op.Table.Predicates) == 0 {
    return errors.New("vindex routing requires a WHERE clause")
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        if strings.Contains(fmt.Sprint(r), "where clause missing") {
            return fmt.Errorf("query needs WHERE clause for vindex routing: %v", r)
        }
        panic(r)
    }
}()

Prevention

When it happens

Trigger: Building a plan for a query that targets a table through the Vindex operator whose v.Table.Predicates is empty when CheckValid() runs — i.e. no WHERE predicate was supplied or extracted.

Common situations: Issuing 'SELECT * FROM t' or a DML without WHERE and expecting vindex routing; an app framework stripping the WHERE clause; a developer testing a vindex-routed table without the required id filter.

Related errors


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