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
- Add a WHERE clause with an equality/IN predicate on the vindex column, e.g. WHERE id = 5.
- If a full scan is intended, do not use this vindex route (use a different routing path).
- 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
- Always include a WHERE predicate on the vindex column when routing by vindex
- Validate user queries reject full-table operations on vindex-routed paths
- Call CheckValid only after predicates have been added via AddPredicate
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.