vitessio/vitess · error

VReplication streams must have named workflows for migration

Error message

VReplication streams must have named workflows for migration: shard: %s:%s

What it means

When preparing to migrate VReplication streams during a reshard, each stream row read from `_vt.vreplication` must have a non-empty workflow name so it can be re-created on the target shards. Older Vitess versions allowed anonymous streams; such a stream makes migration impossible, so the code errors with the offending shard.

Source

Thrown at go/vt/wrangler/resharder.go:216

		mu.Lock()
		defer mu.Unlock()

		mustCreate := false
		var ref map[string]bool
		if rs.refStreams == nil {
			rs.refStreams = make(map[string]*refStream)
			mustCreate = true
		} else {
			// Copy the ref streams for comparison.
			ref = make(map[string]bool, len(rs.refStreams))
			for k := range rs.refStreams {
				ref[k] = true
			}
		}
		for _, row := range qr.Rows {
			workflow := row[0].ToString()
			if workflow == "" {
				return fmt.Errorf("VReplication streams must have named workflows for migration: shard: %s:%s", source.Keyspace(), source.ShardName())
			}
			var bls binlogdatapb.BinlogSource
			rowBytes, err := row[1].ToBytes()
			if err != nil {
				return err
			}
			if err := prototext.Unmarshal(rowBytes, &bls); err != nil {
				return vterrors.Wrapf(err, "prototext.Unmarshal: %v", row)
			}
			isReference, err := rs.blsIsReference(&bls)
			if err != nil {
				return vterrors.Wrap(err, "blsIsReference")
			}
			if !isReference {
				continue
			}
			key := fmt.Sprintf("%s:%s:%s", workflow, bls.Keyspace, bls.Shard)
			if mustCreate {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Find the offending row: `SELECT rid, workflow FROM _vt.vreplication;` on the source shard's primary via vtctldtablet or direct MySQL on the _vt schema.
  2. Give the stream a name (`UPDATE _vt.vreplication SET workflow='<name>' WHERE rid=<id>`) or delete it if it is obsolete (`DELETE FROM _vt.vreplication WHERE rid=<id>`).
  3. Prefer deleting stale legacy streams and recreating them with a named workflow (e.g. via MoveTables/Reshard) if the tooling allows.
  4. Re-run the reshard once no empty-workflow rows remain on any source shard.

Example fix

-- before
SELECT rid, workflow FROM _vt.vreplication; -- rid=1, workflow=''
UPDATE _vt.vreplication SET workflow='legacy_filter' WHERE rid=1;
-- after
SELECT rid, workflow FROM _vt.vreplication; -- rid=1, workflow='legacy_filter'
Defensive patterns

Strategy: validation

Validate before calling

qr, _ := querySourcePrimary("SELECT rid, workflow FROM _vt.vreplication")
for _, row := range qr.Rows {
    if row[1].ToString() == "" {
        return fmt.Errorf("unnamed vreplication stream rid=%s; name or delete before reshard", row[0].ToString())
    }
}

Type guard

func hasNamedWorkflows(rows []sqltypes.Row) bool {
    for _, r := range rows { if r[1].ToString() == "" { return false } }
    return true
}

Try / catch

if err := wr.Reshard(...); err != nil {
    if strings.Contains(err.Error(), "must have named workflows") {
        return fixUnnamedStreamsThenRetry(ctx, source)
    }
    return err
}

Prevention

When it happens

Trigger: Resharding (or any workflow that migrates streams) when a source shard's `_vt.vreplication` table contains a row with an empty `workflow` column — typically a stream created on an older Vitess version before workflows were mandatory.

Common situations: Cluster upgraded from a pre-workflow Vitess release with legacy continuous filter streams left running; a manually inserted vreplication row for testing without a workflow name; half-deleted migration that left an unnamed stream behind.

Related errors


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