vitessio/vitess · error

no tablet picker configured for %s/%s

Error message

no tablet picker configured for %s/%s

What it means

pickSourceTablet needs a TabletPicker (ct.tpTs) to find a healthy serving tablet in the source keyspace/shard. If no tablet picker was configured for the controller (and the source is not an external MySQL), it returns `no tablet picker configured for <keyspace>/<shard>`.

Source

Thrown at go/vt/vttablet/tabletmanager/vreplication/controller.go:384

		Time:    time.Now(),
		Message: message,
	})
	query := fmt.Sprintf("update _vt.vreplication set message=%v where id=%v", encodeString(binlogplayer.MessageTruncate(message)), ct.id)
	if _, err := dbClient.ExecuteFetch(query, 1); err != nil {
		return fmt.Errorf("could not set message: %v: %v", query, err)
	}
	return nil
}

// pickSourceTablet picks a healthy serving tablet to source for
// the vreplication stream. If the source is marked as external, it
// returns nil.
func (ct *controller) pickSourceTablet(ctx context.Context, dbClient binlogplayer.DBClient) (*topodatapb.Tablet, error) {
	if ct.source.GetExternalMysql() != "" {
		return nil, nil
	}
	if ct.tpTs == nil {
		return nil, fmt.Errorf("no tablet picker configured for %s/%s", ct.source.Keyspace, ct.source.Shard)
	}
	log.Info(fmt.Sprintf("%s trying to find an eligible source tablet in %s/%s", ct.logPrefix(), ct.source.Keyspace, ct.source.Shard))

	tablet, err := ct.pickSourceTabletWithIgnoreList(ctx, dbClient)
	// If we failed to find any tablet and we specified an ignore list, then clear the ignore list so that
	// we can try tablets that were previously ignored(since some state may have changed since the last attempt).
	if err != nil && len(ct.ignoreTablets) > 0 {
		log.Info(fmt.Sprintf("%s clearing ignore list of %d tablets and retrying tablet picker",
			ct.logPrefix(), len(ct.ignoreTablets)))
		ct.ignoreTablets = nil
		tablet, err = ct.pickSourceTabletWithIgnoreList(ctx, dbClient)
	}
	if err != nil {
		return tablet, err
	}
	ct.setMessage(dbClient, "Picked source tablet: "+tablet.Alias.String())
	log.Info(fmt.Sprintf("%s found eligible source tablet %s", ct.logPrefix(), tablet.Alias.String()))
	ct.sourceTablet.Store(tablet.Alias)

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Verify the source keyspace and shard exist and have serving tablets in the topology (`vtctldclient GetTablets`).
  2. Check the vttablet flags for cell / topo implementation are correct.
  3. If the source is truly external MySQL, ensure ExternalMysql is set on the stream source so the picker isn't required.
  4. Re-create the workflow with correct source keyspace/shard.

Example fix

// before (stream created against wrong keyspace)
sourceKeyspace: "commerce_old" // does not exist in topo
// after
vtctldclient MoveTables --target-keyspace commerce --tables t1 create commerce_old
Defensive patterns

Strategy: validation

Validate before calling

// Verify serving tablets exist before creating a workflow:
vtctldclient GetTablets --keyspace commerce --shard 0 | grep -i serving
# or in Go, check the topo for source keyspace/shard before constructing the controller

Try / catch

tablet, err := ct.pickSourceTablet(ctx, dbClient)
if err != nil && strings.Contains(err.Error(), "no tablet picker configured") {
    return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "source %s/%s not usable: %w", ks, shard, err)
}

Prevention

When it happens

Trigger: A vreplication stream whose source.Keyspace/Shard is set, ExternalMysql is empty, but the controller was created without a tablet picker timestamp source (ct.tpTs == nil) — typically when the local cell has no topology service or the controller was constructed in a code path that skips picker setup.

Common situations: Misconfigured MoveTables/Reshard workflow where the source keyspace/shard doesn't exist in the topology; vttablet started without proper cell/topo flags; external MySQL handling changed between versions so the external check no longer short-circuits.

Related errors


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