{"record":{"id":"987944e12f2e5cbe","repo":"vitessio/vitess","slug":"vt09001","errorCode":"VT09001","errorMessage":"VT09001(table.Name)","messagePattern":"VT09001\\(table\\.Name\\)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/planbuilder/operators/dml_planning.go","lineNumber":93,"sourceCode":"\t\tvar cols, orderby, limit string\n\t\tcols = fmt.Sprintf(\"COLUMNS: [%s]\", sqlparser.String(ovq.SelectExprs))\n\t\tif len(ovq.OrderBy) > 0 {\n\t\t\torderby = fmt.Sprintf(\" ORDERBY: [%s]\", sqlparser.String(ovq.OrderBy))\n\t\t}\n\t\tif ovq.Limit != nil {\n\t\t\tlimit = fmt.Sprintf(\" LIMIT: [%s]\", sqlparser.String(ovq.Limit))\n\t\t}\n\t\tovqString = fmt.Sprintf(\" vindexQuery(%s%s%s)\", cols, orderby, limit)\n\t}\n\treturn fmt.Sprintf(\"%s.%s%s\", target.VTable.Keyspace.Name, target.VTable.Name.String(), ovqString)\n}\n\n// getVindexInformation returns the vindex and VindexPlusPredicates for the DML,\n// If it cannot find a unique vindex match, it returns an error.\nfunc getVindexInformation(id semantics.TableSet, table *vindexes.BaseTable) *vindexes.ColumnVindex {\n\t// Check that we have a primary vindex which is valid\n\tif len(table.ColumnVindexes) == 0 || !table.ColumnVindexes[0].IsUnique() {\n\t\tpanic(vterrors.VT09001(table.Name))\n\t}\n\treturn table.ColumnVindexes[0]\n}\n","sourceCodeStart":75,"sourceCodeEnd":97,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/planbuilder/operators/dml_planning.go#L75-L97","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before (non-unique vindex first)\n\"column_vindexes\": [{\"name\": \"lookup_idx\", \"columns\": [\"user_id\"]}]\n// after\n\"column_vindexes\": [\n  {\"name\": \"hash\", \"columns\": [\"user_id\"]},\n  {\"name\": \"lookup_idx\", \"columns\": [\"user_id\"]}\n]","handlingStrategy":"validation","validationCode":"curl -s http://vtctld:15000/api/keyspace/<ks> | jq '.vschema[\"tables\"] | to_entries[] | select(.value.column_vindexes | not)'  # tables missing vindexes\n# and confirm column_vindexes[0] references a unique vindex","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["vitess","planbuilder","vindex","sharding"],"backgroundTag":"missing-primary-vindex","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}