vitessio/vitess · error

cannot delete tablet

Error message

cannot delete tablet %v as it is a primary, use allow_primary flag

What it means

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.

Solutions

  1. Confirm the tablet really should be removed; if yes, pass allow_primary (e.g. `vtctldclient DeleteTablet --allow-primary <alias>`)
  2. Better: first reparent or convert the tablet to REPLICA (`vtctldclient PlannedReparentShard` or ChangeTabletType) then delete
  3. Check `vtctldclient GetTablet <alias>` to confirm the tablet's current type before deleting
  4. If the topology record is stale and the tablet is not actually a primary, remove the stale topo entry manually

Example fix

// before: fails on a primary
wr.DeleteTablet(ctx, alias, false)
// after: only delete non-primaries, or opt in explicitly
if isPrimary, _ := wr.isPrimaryTablet(ctx, ti); isPrimary {
	return fmt.Errorf("refusing to delete primary %s without explicit consent", alias)
}
wr.DeleteTablet(ctx, alias, true)
Defensive patterns

Strategy: validation

Validate before calling

ti, err := wr.ts.GetTablet(ctx, alias)
if err != nil {
	return err
}
if ti.Type == topodatapb.TabletType_PRIMARY && !allowPrimary {
	return fmt.Errorf("%s is PRIMARY; pass allow_primary or reparent first", alias)
}

Type guard

func isPrimaryTablet(ti *topodatapb.Tablet) bool {
	return ti != nil && ti.Type == topodatapb.TabletType_PRIMARY
}

Try / catch

if err := wr.DeleteTablet(ctx, alias, false); err != nil {
	if strings.Contains(err.Error(), "use allow_primary flag") {
		// confirm intent, then DeleteTablet(ctx, alias, true) or reparent first
	}
	return err
}

Prevention

When it happens

Trigger: Running `vtctldclient DeleteTablet <alias>` (commandDeleteTablet) on a tablet whose type is PRIMARY without passing allow_primary.

Common situations: 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.

Related errors


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

Appendix: source

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

// Tablet related methods for wrangler

// DeleteTablet removes a tablet from a shard.
// - if allowPrimary is set, we can Delete a primary tablet (and clear
// its record from the Shard record if it was the primary).
func (wr *Wrangler) DeleteTablet(ctx context.Context, tabletAlias *topodatapb.TabletAlias, allowPrimary bool) (err error) {
	// load the tablet, see if we'll need to rebuild
	ti, err := wr.ts.GetTablet(ctx, tabletAlias)
	if err != nil {
		return err
	}

	wasPrimary, err := wr.isPrimaryTablet(ctx, ti)
	if err != nil && !topo.IsErrType(err, topo.NoNode) {
		return err
	}

	if wasPrimary && !allowPrimary {
		return fmt.Errorf("cannot delete tablet %v as it is a primary, use allow_primary flag", topoproto.TabletAliasString(tabletAlias))
	}

	// update the Shard object if the primary was scrapped.
	// we do this before calling DeleteTablet so that the operation can be retried in case of failure.
	if wasPrimary {
		// We lock the shard to not conflict with reparent operations.
		ctx, unlock, lockErr := wr.ts.LockShard(ctx, ti.Keyspace, ti.Shard, fmt.Sprintf("DeleteTablet(%v)", topoproto.TabletAliasString(tabletAlias)))
		if lockErr != nil {
			return lockErr
		}
		defer unlock(&err)

		// update the shard record's primary
		_, err := wr.ts.UpdateShardFields(ctx, ti.Keyspace, ti.Shard, func(si *topo.ShardInfo) error {
			if !topoproto.TabletAliasEqual(si.PrimaryAlias, tabletAlias) {
				wr.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))
				return topo.NewError(topo.NoUpdateNeeded, si.Keyspace()+"/"+si.ShardName())
			}

View on GitHub (pinned to 01a25a7d17)