vitessio/vitess · error

could not set state: %v: %v

Error message

could not set state: %v: %v

What it means

setState persists the stream state (Copying, Running, Error, etc.) via an UPDATE on _vt.vreplication. When not batched inside a transaction and the ExecuteFetch fails, this error wraps the query and MySQL error. A stream whose state cannot be persisted cannot make reliable progress, so it typically halts.

Source

Thrown at go/vt/vttablet/tabletmanager/vreplication/vreplicator.go:588

	insertLog(vr.dbClient, typ, vr.id, vr.state.String(), message)
}

func (vr *vreplicator) setState(state binlogdatapb.VReplicationWorkflowState, message string) error {
	if message != "" {
		vr.stats.History.Add(&binlogplayer.StatsHistoryRecord{
			Time:    time.Now(),
			Message: message,
		})
	}
	vr.stats.State.Store(state.String())
	query := fmt.Sprintf("update _vt.vreplication set state=%v, message=left(%v, 1000) where id=%v", encodeString(state.String()), encodeString(binlogplayer.MessageTruncate(message)), vr.id)
	// If we're batching a transaction, then include the state update
	// in the current transaction batch.
	if vr.dbClient.InTransaction && vr.dbClient.maxBatchSize > 0 {
		vr.dbClient.AddQueryToTrxBatch(query)
	} else { // Otherwise, send it down the wire
		if _, err := vr.dbClient.ExecuteFetch(query, 1); err != nil {
			return fmt.Errorf("could not set state: %v: %v", query, err)
		}
	}
	if state == vr.state {
		return nil
	}
	insertLog(vr.dbClient, LogStateChange, vr.id, state.String(), message)
	vr.state = state

	return nil
}

func encodeString(in string) string {
	return sqltypes.EncodeStringSQL(in)
}

func (vr *vreplicator) getSettingFKCheck() error {
	qr, err := vr.dbClient.Execute("select @@foreign_key_checks")
	if err != nil {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Inspect the wrapped MySQL error: 1290 -> clear read_only/super_read_only; 1146/1032 -> the row was deleted, so re-create or re-register the workflow.
  2. Check tablet-to-MySQL connectivity and failover logs around the failure time.
  3. If workflows were cancelled concurrently, do not force the old stream — re-run MoveTables/Reshard to start a fresh stream.
  4. Retry by restarting the workflow; vreplication streams are resumable from their recorded position.

Example fix

// before: state update fails on read-only target during a failover
// after: ensure target writable before resuming the stream
SET GLOBAL read_only = OFF;
// then restart the workflow:
// vtctldclient Workflow --keyspace=customer start <workflow>
Defensive patterns

Strategy: retry

Validate before calling

// Before resuming a stream, verify target writability and row presence:
// SELECT id, state FROM _vt.vreplication WHERE id = <id>;
// SHOW GLOBAL VARIABLES LIKE 'super_read_only';

Try / catch

if _, err := vr.dbClient.ExecuteFetch(query, 1); err != nil {
	switch mysqlErr.Number() {
	case 1290:
		// target read-only: clear then retry state update
	case 1146, 1032:
		// row deleted: workflow cancelled elsewhere, do not retry
	default:
		// transient: retry with backoff
	}
}

Prevention

When it happens

Trigger: The state UPDATE is sent outside an open transaction batch and MySQL returns an error: row gone (concurrent workflow cancel), read-only mysqld, dead connection, lock timeout. Called by runBlp, play, updatePos, applyEvent and copy-phase code.

Common situations: Concurrent vtctldclient workflow cancellation deleting the row; MySQL failover/read-only mode; network blips between tablet and mysqld; lock contention on the _vt.vreplication row.

Related errors


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