vitessio/vitess · error

tablet %v type change %v -> %v is not an allowed transition

Error message

tablet %v type change %v -> %v is not an allowed transition for ChangeTabletType

What it means

ChangeTabletType only permits 'trivial' type transitions (ones that don't affect replication topology, e.g. replica->rdonly). Requested transitions that would change serving duties — like replica->primary or primary->replica — are rejected with this error because those require reparent operations, not a simple type flip. The error echoes the current and requested types.

Source

Thrown at go/vt/wrangler/tablet.go:102

	}

	return nil
}

// ChangeTabletType changes the type of tablet and recomputes all
// necessary derived paths in the serving graph, if necessary.
//
// Note we don't update the primary record in the Shard here, as we
// can't ChangeType from and out of primary anyway.
func (wr *Wrangler) ChangeTabletType(ctx context.Context, tabletAlias *topodatapb.TabletAlias, tabletType topodatapb.TabletType) error {
	// Load tablet to find endpoint, and keyspace and shard assignment.
	ti, err := wr.ts.GetTablet(ctx, tabletAlias)
	if err != nil {
		return err
	}

	if !topo.IsTrivialTypeChange(ti.Type, tabletType) {
		return fmt.Errorf("tablet %v type change %v -> %v is not an allowed transition for ChangeTabletType", tabletAlias, ti.Type, tabletType)
	}

	// We should clone the tablet and change its type to the expected type before checking the durability rules
	// Since we want to check the durability rules for the desired state and not before we make that change
	expectedTablet := ti.CloneVT()
	expectedTablet.Type = tabletType
	semiSync, err := wr.shouldSendSemiSyncAck(ctx, expectedTablet)
	if err != nil {
		return err
	}
	// and ask the tablet to make the change
	return wr.tmc.ChangeType(ctx, ti.Tablet, tabletType, semiSync)
}

// StartReplication is used to start replication on the specified tablet
// It also finds out if the tablet should be sending semi-sync ACKs or not.
func (wr *Wrangler) StartReplication(ctx context.Context, tablet *topodatapb.Tablet) error {
	semiSync, err := wr.shouldSendSemiSyncAck(ctx, tablet)

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Use `vtctldclient PlannedReparentShard` to promote a replica or demote a primary safely
  2. For demotion during decommission, run ERS/PRS first so the tablet becomes REPLICA, then ChangeTabletType
  3. Only use ChangeTabletType for trivial moves (e.g. REPLICA<->RDONLY/SPARE)
  4. Check the tablet's current type with GetTablet before choosing the operation

Example fix

// before: rejected transition
wr.ChangeTabletType(ctx, alias, topodatapb.TabletType_PRIMARY)
// after: promote via reparent
wr.ts.ReparentShard(ctx, "ks", "-", topoproto.TabletAlias(alias)) // or PlannedReparentShard via vtctldclient
Defensive patterns

Strategy: validation

Validate before calling

ti, err := wr.ts.GetTablet(ctx, alias)
if err != nil {
	return err
}
if !topo.IsTrivialTypeChange(ti.Type, desiredType) {
	return fmt.Errorf("use PlannedReparentShard for %s -> %s", ti.Type, desiredType)
}

Type guard

func isTrivialTransition(from, to topodatapb.TabletType) bool {
	return topo.IsTrivialTypeChange(from, to)
}

Try / catch

if err := wr.ChangeTabletType(ctx, alias, desired); err != nil {
	if strings.Contains(err.Error(), "not an allowed transition") {
		// fall back to reparent workflow for non-trivial moves
	}
	return err
}

Prevention

When it happens

Trigger: Running `vtctldclient ChangeTabletType <alias> <type>` where topo.IsTrivialTypeChange(current, desired) is false, e.g. REPLICA->PRIMARY, PRIMARY->REPLICA, SPARE->PRIMARY.

Common situations: Operators trying to promote a replica to primary directly with ChangeTabletType instead of PlannedReparentShard; scripts demoting a primary by type change after a failed reparent; converting a primary to rdonly for teardown without first demoting it.

Related errors


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