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
- Use the appropriate workflow instead: PlannedReparentShard or EmergencyReparentShard for primary changes
- Only use ChangeTabletType for trivial transitions between REPLICA and RDONLY
- Check topoproto.IsTrivialTypeChange(current, target) client-side before calling
- 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
- Only use ChangeTabletType for REPLICA<->RDONLY transitions
- Use PlannedReparentShard/EmergencyReparentShard for primary changes
- Pre-check IsTrivialTypeChange before the RPC
- Keep tablet topology records in sync with reality
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
- tablet alias is nil
- no app indicated
- failed to get tablet %s from topology: %v
- failed to delete tablet: %w
- cannot find tablet: %+v
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/04504c26fe16549b.
Report an issue: GitHub.