{"record":{"id":"a7e4e39771fc38ae","repo":"vitessio/vitess","slug":"primary-key-d-refers-to-non-existent-column","errorCode":null,"errorMessage":"primary key %d refers to non-existent column","messagePattern":"primary key (.+?) refers to non-existent column","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go","lineNumber":239,"sourceCode":"\t}\n\tpkColumns := make([]int, 0)\n\tif len(st.PKColumns) == 0 {\n\t\t// Use a PK equivalent if one exists.\n\t\tpkColumns, err := rs.vse.mapPKEquivalentCols(rs.ctx, rs.cp, st)\n\t\tif err == nil && len(pkColumns) != 0 {\n\t\t\treturn pkColumns, nil\n\t\t}\n\n\t\t// Fall back to using every column in the table if there's no PK or PKE.\n\t\tpkColumns = make([]int, len(st.Fields))\n\t\tfor i := range st.Fields {\n\t\t\tpkColumns[i] = i\n\t\t}\n\t\treturn pkColumns, nil\n\t}\n\tfor _, pk := range st.PKColumns {\n\t\tif pk >= int64(len(st.Fields)) {\n\t\t\treturn nil, fmt.Errorf(\"primary key %d refers to non-existent column\", pk)\n\t\t}\n\t\tpkColumns = append(pkColumns, int(pk))\n\t}\n\tst.PKIndexName = \"PRIMARY\"\n\treturn pkColumns, nil\n}\n\nfunc (rs *rowStreamer) buildSelect(st *binlogdatapb.MinimalTable) (string, error) {\n\tbuf := sqlparser.NewTrackedBuffer(nil)\n\t// We could have used select *, but being explicit is more predictable.\n\tbuf.Myprintf(\"select \")\n\tif rs.options == nil || !rs.options.NoTimeouts { // We don't e.g. want to add the timeout for a VDiff query\n\t\tbuf.Myprintf(\"%s\", GetVReplicationMaxExecutionTimeQueryHint(rs.config.CopyPhaseDuration))\n\t}\n\tprefix := \"\"\n\tfor _, col := range rs.plan.Table.Fields {\n\t\tif rs.plan.isConvertColumnUsingUTF8(col.Name) {\n\t\t\tbuf.Myprintf(\"%sconvert(%v using utf8mb4) as %v\", prefix, sqlparser.NewIdentifierCI(col.Name), sqlparser.NewIdentifierCI(col.Name))","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go#L221-L257","documentation":"buildPKColumns validates that every primary key column index stored in the table's statement/plan metadata actually exists within the decoded field list. If a PK index exceeds the number of fields, the schema information is inconsistent and the row streamer cannot build a plan.","triggerScenarios":"st.PKColumns contains an index >= len(st.Fields) when building the row streamer plan — i.e. schema metadata claims more/positioned PK columns than the field set returned for the table.","commonSituations":"Table schema changed (column dropped / PK altered) between schema load and field decoding, corrupted or stale table metadata, or a parser/analyzer bug producing mismatched PK column positions.","solutions":["Compare the table's SHOW CREATE TABLE with the fields the streamer decoded; if the schema changed, refresh schema on the tablet (ReloadSchema) and restart the workflow.","Verify the source table actually has a PRIMARY KEY matching the metadata; if the PK was dropped/altered mid-stream, re-plan the VStream from scratch.","If it persists with an unchanged schema, capture the table name and plan and file a bug with reproduction details — this indicates internal metadata inconsistency."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// before streaming, confirm PK metadata matches live schema\nfields := getTableFields(tableName)      // from schema\npkCols := getPrimaryKeyColumns(tableName) // SHOW INDEX / information_schema\nif len(pkCols) == 0 || len(pkCols) > len(fields) {\n    return fmt.Errorf(\"table %s PK metadata inconsistent: %d pk cols vs %d fields\", tableName, len(pkCols), len(fields))\n}","typeGuard":null,"tryCatchPattern":"if err := buildPlan(...); err != nil {\n    if strings.Contains(err.Error(), \"refers to non-existent column\") {\n        // refresh schema and re-plan\n        tablet.ReloadSchema(ctx)\n        plan, err = buildPlan(...) // retry once\n    }\n}","preventionTips":["Avoid ALTER TABLE on PKs while VReplication streams are active","Reload tablet schema after DDL and before starting workflows","Ensure source tables have well-defined PRIMARY KEYs","Pin schema changes to maintenance windows for replicated tables"],"tags":["vstreamer","rowstreamer","schema-mismatch"],"backgroundTag":"primary-key-schema-mismatch","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}