vitessio/vitess · error

invalid type transition %v: %v -> %v

Error message

invalid type transition %v: %v -> %v

What it means

During a dry-run ChangeTabletType, Vitess checks topo.IsTrivialTypeChange between the tablet's current type and the requested type. If the transition is not considered trivially safe to simulate/apply, this error names the alias and both types.

Source

Thrown at go/vt/vtctl/vtctl.go:1160

	tabletAlias, err := topoproto.ParseTabletAlias(subFlags.Arg(0))
	if err != nil {
		return err
	}
	newType, err := parseTabletType(subFlags.Arg(1), topoproto.AllTabletTypes)
	if err != nil {
		return err
	}

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

	if *dryRun {
		ti, err := wr.TopoServer().GetTablet(ctx, tabletAlias)
		if err != nil {
			return fmt.Errorf("failed reading tablet %v: %v", tabletAlias, err)
		}
		if !topo.IsTrivialTypeChange(ti.Type, newType) {
			return fmt.Errorf("invalid type transition %v: %v -> %v", tabletAlias, ti.Type, newType)
		}
		wr.Logger().Printf("- %v\n", fmtTabletAwkable(ti))
		ti.Type = newType
		wr.Logger().Printf("+ %v\n", fmtTabletAwkable(ti))
		return nil
	}
	return wr.ChangeTabletType(ctx, tabletAlias, newType)
}

func commandPing(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.FlagSet, args []string) error {
	if err := subFlags.Parse(args); err != nil {
		return err
	}
	if subFlags.NArg() != 1 {
		return errors.New("the <tablet alias> argument is required for the Ping command")
	}
	tabletAlias, err := topoproto.ParseTabletAlias(subFlags.Arg(0))
	if err != nil {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Check the tablet's current type (`vtctl GetTablet <alias>`) and pick a trivially-allowed target
  2. Use PlannedReparentShard/ EmergencyReparentShard for master transitions
  3. Wait for BACKUP/RESTORE tablets to finish before changing type
  4. Update automation to account for the IsTrivialTypeChange rules

Example fix

// before
vtctl ChangeTabletType -dry-run zone1-0000000100 RDONLY   # tablet is MASTER
// after
vtctl PlannedReparentShard commerce/0 zone1-0000000101
vtctl ChangeTabletType -dry-run zone1-0000000100 RDONLY
Defensive patterns

Strategy: validation

Validate before calling

CUR=$(vtctl GetTablet -format json "$ALIAS" | jq -r '.type')
# refuse non-trivial transitions up front
[[ "$CUR" == "MASTER" ]] && { echo "use reparent workflow, not ChangeTabletType"; exit 1; }
[[ "$CUR" == "BACKUP" || "$CUR" == "RESTORE" ]] && { echo "tablet busy: $CUR"; exit 1; }

Try / catch

if err := runVtctl("ChangeTabletType", "--dry-run", alias, tt); err != nil {
    if strings.Contains(err.Error(), "invalid type transition") {
        // fetch current type, choose a reparent/backup-aware path
    }
}

Prevention

When it happens

Trigger: Running `vtctl ChangeTabletType --dry-run <alias> <newType>` where the current→new transition is not a trivial type change — e.g. attempting to change a MASTER tablet, or a transition the command considers invalid.

Common situations: Scripts that attempt master changes via ChangeTabletType instead of reparent workflows; changing a BACKUP/RESTORE tablet mid-operation; automations assuming all transitions are allowed.

Related errors


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