vitessio/vitess · error

could not set message: %v: %v

Error message

could not set message: %v: %v

What it means

setMessage writes a human-readable status message into _vt.vreplication.message for this stream. If the UPDATE fails, this error wraps the query and the MySQL error. It usually signals the vreplication row is missing or the target MySQL is rejecting writes.

Source

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

	}
	numTablesToCopy, err = qr.Rows[0][0].ToCastInt64()
	if err != nil {
		return settings, numTablesToCopy, err
	}
	return settings, numTablesToCopy, nil
}

func (vr *vreplicator) setMessage(message string) (err error) {
	message = binlogplayer.MessageTruncate(message)
	vr.stats.History.Add(&binlogplayer.StatsHistoryRecord{
		Time:    time.Now(),
		Message: message,
	})
	buf := sqlparser.NewTrackedBuffer(nil)
	buf.Myprintf("update _vt.vreplication set message=%s where id=%s", encodeString(message), strconv.Itoa(int(vr.id)))
	query := buf.ParsedQuery().Query
	if _, err := vr.dbClient.Execute(query); err != nil {
		return fmt.Errorf("could not set message: %v: %v", query, err)
	}
	insertLog(vr.dbClient, LogMessage, vr.id, vr.state.String(), message)
	return nil
}

func (vr *vreplicator) maxQuerySize(dbc *vdbClient) int64 {
	maxQuerySize := int64(vr.workflowConfig.RelayLogMaxSize)
	res, err := dbc.DBClient.ExecuteFetch(SqlMaxAllowedPacket, 1)
	if err != nil {
		log.Error(fmt.Sprintf("Error getting max_allowed_packet, will use the relay-log-max-size value of %d bytes: %v", vr.workflowConfig.RelayLogMaxSize, err))
	} else {
		if maxQuerySize, err = res.Rows[0][0].ToInt64(); err != nil {
			log.Error(fmt.Sprintf("Error getting max_allowed_packet, will use the relay-log-max-size value of %d bytes: %v", vr.workflowConfig.RelayLogMaxSize, err))
			maxQuerySize = int64(vr.workflowConfig.RelayLogMaxSize)
		}
	}
	if maxQuerySize > maxQuerySizeHeadroom {
		maxQuerySize -= maxQuerySizeHeadroom

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Check the wrapped MySQL error: 1290 (read-only) means the target mysqld is in super_read_only — clear it; 1146/1032 means the row is gone.
  2. Verify the _vt.vreplication row for this id still exists; if the workflow was cancelled elsewhere, stop this vreplicator instead of forcing writes.
  3. Check for lock contention on _vt.vreplication from other workflows/clients and retry after it clears.
  4. Restart the workflow (or the tablet) so the stream re-reads its state.

Example fix

// before: target in super_read_only during maintenance
// after: restore writability on the target mysqld
SET GLOBAL super_read_only = OFF;
SET GLOBAL read_only = OFF;
Defensive patterns

Strategy: retry

Validate before calling

// Confirm the row exists and the target is writable:
// SELECT id FROM _vt.vreplication WHERE id = <id>;
// SHOW GLOBAL VARIABLES LIKE 'read_only';   -- expect OFF
// SHOW GLOBAL VARIABLES LIKE 'super_read_only';

Try / catch

if _, err := vr.dbClient.Execute(query); err != nil {
	// inspect mysql error code: 1290 -> read-only; 1146/1032 -> row gone
	log.Warn("could not set vreplication message", slog.Any("error", err))
	// retry after read-only is cleared, or abandon the cancelled workflow
}

Prevention

When it happens

Trigger: The 'update _vt.vreplication set message=... where id=<id>' statement fails — e.g. the row was deleted concurrently, MySQL is read-only (super_read_only), or a lock/timeout kills the statement. Called from Replicate during message updates.

Common situations: Workflow row removed by a concurrent MoveTables/Reshard cancel while the stream is still running; target tablet MySQL flipped to read-only during maintenance; long lock hold on the _vt.vreplication row.

Related errors


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