vitessio/vitess · error

invalid state found for vdiff table %s for vdiff_id %d on ta

Error message

invalid state found for vdiff table %s for vdiff_id %d on tablet %s

What it means

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`.

Source

Thrown at go/vt/vttablet/tabletmanager/vdiff/workflow_differ.go:611

			query, err = sqlparser.ParseAndBind(sqlNewVDiffTable,
				sqltypes.Int64BindVariable(wd.ct.id),
				sqltypes.StringBindVariable(tableName),
				sqltypes.Int64BindVariable(tableRows),
			)
			if err != nil {
				return err
			}
		} else if len(qr.Rows) == 1 {
			query, err = sqlparser.ParseAndBind(sqlUpdateTableRows,
				sqltypes.Int64BindVariable(tableRows),
				sqltypes.Int64BindVariable(wd.ct.id),
				sqltypes.StringBindVariable(tableName),
			)
			if err != nil {
				return err
			}
		} else {
			return fmt.Errorf("invalid state found for vdiff table %s for vdiff_id %d on tablet %s",
				tableName, wd.ct.id, wd.ct.vde.thisTablet.Alias)
		}
		if _, err := dbClient.ExecuteFetch(query, 1); err != nil {
			return err
		}
	}
	return nil
}

// getSourceTopoServer returns the source topo server as for Mount+Migrate the
// source tablets will be in a different Vitess cluster with its own TopoServer.
func (wd *workflowDiffer) getSourceTopoServer() (*topo.Server, error) {
	if wd.ct.externalCluster == "" {
		return wd.ct.ts, nil
	}
	ctx, cancel := context.WithTimeout(wd.ct.vde.ctx, topo.RemoteOperationTimeout)
	defer cancel()
	return wd.ct.ts.OpenExternalVitessClusterServer(ctx, wd.ct.externalCluster)

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Cancel any concurrent vdiffs on the workflow and ensure only one runs at a time
  2. Delete the duplicate rows from _vt.vdiff_table on the target primary (keep one row per table per vdiff id), then restart the vdiff
  3. Cleanest option: cancel the vdiff and start a fresh one so all table rows are recreated
  4. Check the Vitess version for known vdiff state bugs and upgrade if applicable

Example fix

// before (duplicates present)
SELECT * FROM _vt.vdiff_table WHERE vdiff_id=1 AND table_name='t'; -- 2 rows
// after
cancel vdiff; delete dup rows (keep 1); re-run:
DELETE vt1 FROM _vt.vdiff_table vt1 JOIN _vt.vdiff_table vt2
  ON vt1.vdiff_id=vt2.vdiff_id AND vt1.table_name=vt2.table_name
  AND vt1.row_id > vt2.rowid WHERE vt1.vdiff_id=1;
Defensive patterns

Strategy: type-guard

Validate before calling

// before starting a vdiff, assert no duplicate table rows exist
qr, _ := dbClient.ExecuteFetch(fmt.Sprintf(`SELECT table_name, COUNT(*) c
  FROM _vt.vdiff_table WHERE vdiff_id=%d GROUP BY table_name HAVING c>1`, id), -1)
if len(qr.Rows) > 0 { // duplicates: clean up or cancel/restart the vdiff }

Type guard

func vdiffTableStateIsClean(dbClient binlogplayer.DBClient, vdiffID int64) bool {
  qr, err := dbClient.ExecuteFetch(fmt.Sprintf(
    `SELECT table_name, COUNT(*) c FROM _vt.vdiff_table WHERE vdiff_id=%d GROUP BY table_name HAVING c>1`, vdiffID), -1)
  return err == nil && len(qr.Rows) == 0
}

Try / catch

if err := wd.diff(ctx, dbClient); err != nil {
  if strings.Contains(err.Error(), "invalid state found for vdiff table") {
    return fmt.Errorf("duplicate _vt.vdiff_table rows; cancel vdiff, clean duplicates, rerun: %w", err)
  }
  return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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