vitessio/vitess · error
VT09001
VT09001
Error message
VT09001(table.Name)
What it means
VT09001 means the target table of an UPDATE or DELETE has no usable primary vindex. getVindexInformation requires the table's first ColumnVindex to exist and be unique (it is the sharding key used to route the DML); if the table is unsharded-mapped or its primary vindex is non-unique, the planner panics with this error naming the table.
Source
Thrown at go/vt/vtgate/planbuilder/operators/dml_planning.go:93
var cols, orderby, limit string
cols = fmt.Sprintf("COLUMNS: [%s]", sqlparser.String(ovq.SelectExprs))
if len(ovq.OrderBy) > 0 {
orderby = fmt.Sprintf(" ORDERBY: [%s]", sqlparser.String(ovq.OrderBy))
}
if ovq.Limit != nil {
limit = fmt.Sprintf(" LIMIT: [%s]", sqlparser.String(ovq.Limit))
}
ovqString = fmt.Sprintf(" vindexQuery(%s%s%s)", cols, orderby, limit)
}
return fmt.Sprintf("%s.%s%s", target.VTable.Keyspace.Name, target.VTable.Name.String(), ovqString)
}
// getVindexInformation returns the vindex and VindexPlusPredicates for the DML,
// If it cannot find a unique vindex match, it returns an error.
func getVindexInformation(id semantics.TableSet, table *vindexes.BaseTable) *vindexes.ColumnVindex {
// Check that we have a primary vindex which is valid
if len(table.ColumnVindexes) == 0 || !table.ColumnVindexes[0].IsUnique() {
panic(vterrors.VT09001(table.Name))
}
return table.ColumnVindexes[0]
}
View on GitHub (pinned to 01a25a7d17)
Solutions
- Define a primary, unique vindex (e.g. hash) for the table in the vschema and ensure it is the first entry in column_vindexes
- Verify with `vtctldclient GetVSchema <keyspace>` that the table's column_vindexes[0] uses a unique vindex
- If the table should be routed as unsharded/interleaved, correct the routing rules or target the right keyspace instead of relying on vindex lookup
Example fix
// before (non-unique vindex first)
"column_vindexes": [{"name": "lookup_idx", "columns": ["user_id"]}]
// after
"column_vindexes": [
{"name": "hash", "columns": ["user_id"]},
{"name": "lookup_idx", "columns": ["user_id"]}
] Defensive patterns
Strategy: validation
Validate before calling
curl -s http://vtctld:15000/api/keyspace/<ks> | jq '.vschema["tables"] | to_entries[] | select(.value.column_vindexes | not)' # tables missing vindexes # and confirm column_vindexes[0] references a unique vindex
Prevention
- Always declare a primary unique vindex (first in column_vindexes) for sharded tables
- Validate vschema with vtctldclient/GetVSchema before deploying
- Add integration tests issuing UPDATE/DELETE against every sharded table
When it happens
Trigger: Calling UPDATE or DELETE through VTGate on a table whose vindexes list is empty, or whose first (primary) vindex is non-unique (e.g. a lookup vindex marked non-unique was declared first in the VReplication/vindex config).
Common situations: Mis-ordered vindexes in the keyspace's vschema JSON (non-unique vindex placed first); tables created before vindex definitions were applied; mistyping the vindex name so no ColumnVindexes are attached; moving a table from unsharded to sharded without adding a primary vindex.
Related errors
- VT12002
- no sharded vschema was provided, so you will need to update
- only integer literals are supported
- only the integer literal 1 is supported
- cannot find column to use to find keyspace_id for table %v
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/987944e12f2e5cbe.
Report an issue: GitHub.