vitessio/vitess · error

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

Error message

no state found for vdiff table %s for vdiff_id %d on tablet %v

What it means

Before diffing a table, the differ loads the saved state row for (vdiff_id, table) from _vt.vdiff_table. If the query returns zero rows there is no prior state (last PK, counts, etc.) to continue from, so diff aborts with this error.

Source

Thrown at go/vt/vttablet/tabletmanager/vdiff/table_differ.go:544

	}
	defer dbClient.Close()

	// We need to continue were we left off when appropriate. This can be an
	// auto-retry on error, or a manual retry via the resume command.
	// Otherwise the existing state will be empty and we start from scratch.
	query, err := sqlparser.ParseAndBind(sqlGetVDiffTable,
		sqltypes.Int64BindVariable(td.wd.ct.id),
		sqltypes.StringBindVariable(td.table.Name),
	)
	if err != nil {
		return nil, err
	}
	cs, err := dbClient.ExecuteFetch(query, -1)
	if err != nil {
		return nil, err
	}
	if len(cs.Rows) == 0 {
		return nil, fmt.Errorf("no state found for vdiff table %s for vdiff_id %d on tablet %v",
			td.table.Name, td.wd.ct.id, td.wd.ct.vde.thisTablet.Alias)
	} else if len(cs.Rows) > 1 {
		return nil, fmt.Errorf("invalid state found for vdiff table %s (multiple records) for vdiff_id %d on tablet %v",
			td.table.Name, td.wd.ct.id, td.wd.ct.vde.thisTablet.Alias)
	}
	curState := cs.Named().Row()
	mismatch := curState.AsBool("mismatch", false)
	dr := &DiffReport{}
	if rpt := curState.AsBytes("report", []byte("{}")); json.Valid(rpt) {
		if err = json.Unmarshal(rpt, dr); err != nil {
			return nil, err
		}
	}
	dr.TableName = td.table.Name

	// Scope executor goroutines to this single diff attempt, rather
	// than surviving until the controller context is canceled.
	execCtx, cancelExec := context.WithCancel(ctx)

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Delete the vdiff and create a fresh one so state rows are properly inserted: vtctldclient vdiff ... delete all, then create.
  2. Verify rows exist: SELECT * FROM _vt.vdiff_table WHERE vdiff_id=<id> AND table_name='<t>'; on the tablet.
  3. Do not manually remove _vt.vdiff_table rows while a vdiff is active.

Example fix

// before
DELETE FROM _vt.vdiff_table WHERE vdiff_id=7; // while vdiff 7 running
// after
vtctldclient vdiff --keyspace ks --workflow wf delete all
vtctldclient vdiff --keyspace ks --workflow wf create // reinserts state
Defensive patterns

Strategy: validation

Validate before calling

var n int
_ := db.QueryRow("SELECT COUNT(*) FROM _vt.vdiff_table WHERE vdiff_id=? AND table_name=?", id, table).Scan(&n)
if n == 0 { recreateVDiff() }

Try / catch

if err != nil && strings.Contains(err.Error(), "no state found for vdiff table") {
  // state row missing; delete all + create a fresh vdiff
  deleteAllVDiffs(workflow)
  createVDiff(keyspace, workflow)
}

Prevention

When it happens

Trigger: diff → table state query returns len(cs.Rows)==0 because the _vt.vdiff_table row for this vdiff id/table is missing — deleted manually, or the vdiff record was recreated with a new id while an old controller ran.

Common situations: Manual cleanup of _vt.vdiff_table while a vdiff runs; controller retry after the row was removed by 'vdiff delete all'; replica tablet with truncated/missing _vt.vdiff_table data.

Related errors


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