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

  1. Define a primary, unique vindex (e.g. hash) for the table in the vschema and ensure it is the first entry in column_vindexes
  2. Verify with `vtctldclient GetVSchema <keyspace>` that the table's column_vindexes[0] uses a unique vindex
  3. 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

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


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