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 (e.g. replica->rdonly or rdonly->replica) because the vttablet process must be restarted for non-trivial type changes. When the requested target type (req.DbType) is not a trivial transition from the tablet's current type, the vtctld server rejects the request with this plain fmt.Errorf before contacting the tablet.

Source

Thrown at go/vt/vtctl/grpcvtctldserver/server.go:665

	defer panicHandler(&err)

	span.Annotate("tablet_alias", topoproto.TabletAliasString(req.TabletAlias))
	span.Annotate("dry_run", req.DryRun)
	span.Annotate("tablet_type", topoproto.TabletTypeLString(req.DbType))

	ctx, cancel := context.WithTimeout(ctx, topo.RemoteOperationTimeout)
	defer cancel()

	tablet, err := s.ts.GetTablet(ctx, req.TabletAlias)
	if err != nil {
		return nil, err
	}

	span.Annotate("before_tablet_type", topoproto.TabletTypeLString(tablet.Type))

	if !topo.IsTrivialTypeChange(tablet.Type, req.DbType) {
		err = fmt.Errorf("tablet %v type change %v -> %v is not an allowed transition for ChangeTabletType", req.TabletAlias, tablet.Type, req.DbType)
		return nil, err
	}

	if req.DryRun {
		afterTablet := tablet.CloneVT()
		afterTablet.Type = req.DbType

		return &vtctldatapb.ChangeTabletTypeResponse{
			BeforeTablet: tablet.Tablet,
			AfterTablet:  afterTablet,
			WasDryRun:    true,
		}, nil
	}

	shard, err := s.ts.GetShard(ctx, tablet.Keyspace, tablet.Shard)
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Use the appropriate workflow instead: PlannedReparentShard or EmergencyReparentShard for primary changes
  2. Only use ChangeTabletType for trivial transitions between REPLICA and RDONLY
  3. Check topoproto.IsTrivialTypeChange(current, target) client-side before calling
  4. If the tablet's recorded type is stale, fix the topology record first (e.g. via TabletExternallyReparented or deleting/re-adding the tablet record)

Example fix

// before
client.ChangeTabletType(ctx, alias, topodatapb.TabletType_PRIMARY)
// after
client.PlannedReparentShard(ctx, "commerce", "0", alias)
Defensive patterns

Strategy: validation

Validate before calling

if !topo.IsTrivialTypeChange(tablet.Type, reqType) {
    return fmt.Errorf("use PlannedReparentShard for %v -> %v", tablet.Type, reqType)
}

Type guard

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

Try / catch

resp, err := client.ChangeTabletType(ctx, alias, tt)
if err != nil && strings.Contains(err.Error(), "not an allowed transition") {
    // fall back to PlannedReparentShard
}

Prevention

When it happens

Trigger: Calling vtctldserver ChangeTabletType with a DbType such as PRIMARY, SPARE, or another non-trivial transition from the tablet's current type; e.g. changing a replica to PRIMARY via ChangeTabletType instead of PlannedReparentShard.

Common situations: Scripts or operators using ChangeTabletType to promote/demote primaries; automation written against older Vitess where more transitions were allowed; mixing up ChangeTabletType with Reparenting workflows.

Related errors


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