{"record":{"id":"ead05d3d0648469a","repo":"vitessio/vitess","slug":"invalid-state-found-for-vdiff-table-s-for-vdiff-i","errorCode":null,"errorMessage":"invalid state found for vdiff table %s for vdiff_id %d on tablet %s","messagePattern":"invalid state found for vdiff table (.+?) for vdiff_id (.+?) on tablet (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vttablet/tabletmanager/vdiff/workflow_differ.go","lineNumber":611,"sourceCode":"\t\t\tquery, err = sqlparser.ParseAndBind(sqlNewVDiffTable,\n\t\t\t\tsqltypes.Int64BindVariable(wd.ct.id),\n\t\t\t\tsqltypes.StringBindVariable(tableName),\n\t\t\t\tsqltypes.Int64BindVariable(tableRows),\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else if len(qr.Rows) == 1 {\n\t\t\tquery, err = sqlparser.ParseAndBind(sqlUpdateTableRows,\n\t\t\t\tsqltypes.Int64BindVariable(tableRows),\n\t\t\t\tsqltypes.Int64BindVariable(wd.ct.id),\n\t\t\t\tsqltypes.StringBindVariable(tableName),\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\treturn fmt.Errorf(\"invalid state found for vdiff table %s for vdiff_id %d on tablet %s\",\n\t\t\t\ttableName, wd.ct.id, wd.ct.vde.thisTablet.Alias)\n\t\t}\n\t\tif _, err := dbClient.ExecuteFetch(query, 1); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// getSourceTopoServer returns the source topo server as for Mount+Migrate the\n// source tablets will be in a different Vitess cluster with its own TopoServer.\nfunc (wd *workflowDiffer) getSourceTopoServer() (*topo.Server, error) {\n\tif wd.ct.externalCluster == \"\" {\n\t\treturn wd.ct.ts, nil\n\t}\n\tctx, cancel := context.WithTimeout(wd.ct.vde.ctx, topo.RemoteOperationTimeout)\n\tdefer cancel()\n\treturn wd.ct.ts.OpenExternalVitessClusterServer(ctx, wd.ct.externalCluster)","sourceCodeStart":593,"sourceCodeEnd":629,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vttablet/tabletmanager/vdiff/workflow_differ.go#L593-L629","documentation":"initVDiffTables reads existing _vt.vdiff_table rows for each table. It expects 0 rows (insert a new one) or exactly 1 row (update row count). Finding more than one row for the same (vdiff_id, table_name) means corrupted/duplicated state, so it throws `invalid state found for vdiff table`.","triggerScenarios":"Duplicate rows in _vt.vdiff_table for the same vdiff id and table name — caused by concurrent vdiff operations on the same workflow, retried inserts without unique constraints being honored, or manual manipulation of _vt tables.","commonSituations":"Running two vdiffs simultaneously against the same workflow; a previously failed vdiff left duplicate rows; DBA manually inserted rows into _vt.vdiff_table; older Vitess version bug that allowed duplicates.","solutions":["Cancel any concurrent vdiffs on the workflow and ensure only one runs at a time","Delete the duplicate rows from _vt.vdiff_table on the target primary (keep one row per table per vdiff id), then restart the vdiff","Cleanest option: cancel the vdiff and start a fresh one so all table rows are recreated","Check the Vitess version for known vdiff state bugs and upgrade if applicable"],"exampleFix":"// before (duplicates present)\nSELECT * FROM _vt.vdiff_table WHERE vdiff_id=1 AND table_name='t'; -- 2 rows\n// after\ncancel vdiff; delete dup rows (keep 1); re-run:\nDELETE vt1 FROM _vt.vdiff_table vt1 JOIN _vt.vdiff_table vt2\n  ON vt1.vdiff_id=vt2.vdiff_id AND vt1.table_name=vt2.table_name\n  AND vt1.row_id > vt2.rowid WHERE vt1.vdiff_id=1;","handlingStrategy":"type-guard","validationCode":"// before starting a vdiff, assert no duplicate table rows exist\nqr, _ := dbClient.ExecuteFetch(fmt.Sprintf(`SELECT table_name, COUNT(*) c\n  FROM _vt.vdiff_table WHERE vdiff_id=%d GROUP BY table_name HAVING c>1`, id), -1)\nif len(qr.Rows) > 0 { // duplicates: clean up or cancel/restart the vdiff }","typeGuard":"func vdiffTableStateIsClean(dbClient binlogplayer.DBClient, vdiffID int64) bool {\n  qr, err := dbClient.ExecuteFetch(fmt.Sprintf(\n    `SELECT table_name, COUNT(*) c FROM _vt.vdiff_table WHERE vdiff_id=%d GROUP BY table_name HAVING c>1`, vdiffID), -1)\n  return err == nil && len(qr.Rows) == 0\n}","tryCatchPattern":"if err := wd.diff(ctx, dbClient); err != nil {\n  if strings.Contains(err.Error(), \"invalid state found for vdiff table\") {\n    return fmt.Errorf(\"duplicate _vt.vdiff_table rows; cancel vdiff, clean duplicates, rerun: %w\", err)\n  }\n  return err\n}","preventionTips":["Never run two vdiffs concurrently on the same workflow","Don't manually insert/delete rows in _vt.vdiff_table","Cancel a stuck vdiff before starting a new one","Upgrade past known Vitess versions with vdiff state bugs"],"tags":["vdiff","state","duplicate-rows"],"backgroundTag":"vdiff-corrupt-table-state","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}