{"record":{"id":"818dbd3f68cd46a3","repo":"vitessio/vitess","slug":"cannot-delete-tablet-v-as-it-is-a-primary-use-al","errorCode":null,"errorMessage":"cannot delete tablet %v as it is a primary, use allow_primary flag","messagePattern":"cannot delete tablet (.+?) as it is a primary, use allow_primary flag","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/wrangler/tablet.go","lineNumber":53,"sourceCode":"// Tablet related methods for wrangler\n\n// DeleteTablet removes a tablet from a shard.\n// - if allowPrimary is set, we can Delete a primary tablet (and clear\n// its record from the Shard record if it was the primary).\nfunc (wr *Wrangler) DeleteTablet(ctx context.Context, tabletAlias *topodatapb.TabletAlias, allowPrimary bool) (err error) {\n\t// load the tablet, see if we'll need to rebuild\n\tti, err := wr.ts.GetTablet(ctx, tabletAlias)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\twasPrimary, err := wr.isPrimaryTablet(ctx, ti)\n\tif err != nil && !topo.IsErrType(err, topo.NoNode) {\n\t\treturn err\n\t}\n\n\tif wasPrimary && !allowPrimary {\n\t\treturn fmt.Errorf(\"cannot delete tablet %v as it is a primary, use allow_primary flag\", topoproto.TabletAliasString(tabletAlias))\n\t}\n\n\t// update the Shard object if the primary was scrapped.\n\t// we do this before calling DeleteTablet so that the operation can be retried in case of failure.\n\tif wasPrimary {\n\t\t// We lock the shard to not conflict with reparent operations.\n\t\tctx, unlock, lockErr := wr.ts.LockShard(ctx, ti.Keyspace, ti.Shard, fmt.Sprintf(\"DeleteTablet(%v)\", topoproto.TabletAliasString(tabletAlias)))\n\t\tif lockErr != nil {\n\t\t\treturn lockErr\n\t\t}\n\t\tdefer unlock(&err)\n\n\t\t// update the shard record's primary\n\t\t_, err := wr.ts.UpdateShardFields(ctx, ti.Keyspace, ti.Shard, func(si *topo.ShardInfo) error {\n\t\t\tif !topoproto.TabletAliasEqual(si.PrimaryAlias, tabletAlias) {\n\t\t\t\twr.Logger().Warningf(\"Deleting primary %v from shard %v/%v but primary in Shard object was %v\", topoproto.TabletAliasString(tabletAlias), ti.Keyspace, ti.Shard, topoproto.TabletAliasString(si.PrimaryAlias))\n\t\t\t\treturn topo.NewError(topo.NoUpdateNeeded, si.Keyspace()+\"/\"+si.ShardName())\n\t\t\t}","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/wrangler/tablet.go#L35-L71","documentation":"DeleteTablet refuses to delete a tablet that is currently the shard primary unless the allowPrimary flag is explicitly set. This is a safety guard against accidentally removing a serving primary from the topology. The check queries the tablet type and only errors when wasPrimary is true and allowPrimary is false.","triggerScenarios":"Running `vtctldclient DeleteTablet <alias>` (commandDeleteTablet) on a tablet whose type is PRIMARY without passing allow_primary.","commonSituations":"Decommissioning tablets after a reparent where the old primary hasn't been converted to replica; scripted cleanup that blindly deletes all tablet aliases including the current primary; mistaken alias ordering when purging a shard.","solutions":["Confirm the tablet really should be removed; if yes, pass allow_primary (e.g. `vtctldclient DeleteTablet --allow-primary <alias>`)","Better: first reparent or convert the tablet to REPLICA (`vtctldclient PlannedReparentShard` or ChangeTabletType) then delete","Check `vtctldclient GetTablet <alias>` to confirm the tablet's current type before deleting","If the topology record is stale and the tablet is not actually a primary, remove the stale topo entry manually"],"exampleFix":"// before: fails on a primary\nwr.DeleteTablet(ctx, alias, false)\n// after: only delete non-primaries, or opt in explicitly\nif isPrimary, _ := wr.isPrimaryTablet(ctx, ti); isPrimary {\n\treturn fmt.Errorf(\"refusing to delete primary %s without explicit consent\", alias)\n}\nwr.DeleteTablet(ctx, alias, true)","handlingStrategy":"validation","validationCode":"ti, err := wr.ts.GetTablet(ctx, alias)\nif err != nil {\n\treturn err\n}\nif ti.Type == topodatapb.TabletType_PRIMARY && !allowPrimary {\n\treturn fmt.Errorf(\"%s is PRIMARY; pass allow_primary or reparent first\", alias)\n}","typeGuard":"func isPrimaryTablet(ti *topodatapb.Tablet) bool {\n\treturn ti != nil && ti.Type == topodatapb.TabletType_PRIMARY\n}","tryCatchPattern":"if err := wr.DeleteTablet(ctx, alias, false); err != nil {\n\tif strings.Contains(err.Error(), \"use allow_primary flag\") {\n\t\t// confirm intent, then DeleteTablet(ctx, alias, true) or reparent first\n\t}\n\treturn err\n}","preventionTips":["Always GetTablet to check type before deletion","Demote primaries via PlannedReparentShard before decommissioning","Gate allow_primary behind explicit operator consent in scripts"],"tags":["tablet-management","topology","safety-guard"],"backgroundTag":"delete-primary-tablet-blocked","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}