vitessio/vitess · error · ErrUnauthorized
%w: cannot delete shards in %s
Error message
%w: cannot delete shards in %s
What it means
VTAdmin's DeleteShards RPC returns this when RBAC denies the caller the 'delete' action on the Shard resource in the requested cluster. The API fails fast before contacting the cluster, wrapping errors.ErrUnauthorized. Shard deletion is deliberately gated separately from shard creation.
Source
Thrown at go/vt/vtadmin/api.go:672
}
c, err := api.getClusterForRequest(req.ClusterId)
if err != nil {
return nil, err
}
return c.DeleteKeyspace(ctx, req.Options)
}
// DeleteShards is part of the vtadminpb.VTAdminServer interface.
func (api *API) DeleteShards(ctx context.Context, req *vtadminpb.DeleteShardsRequest) (*vtctldatapb.DeleteShardsResponse, error) {
span, ctx := trace.NewSpan(ctx, "API.DeleteShards")
defer span.Finish()
span.Annotate("cluster_id", req.ClusterId)
if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.ShardResource, rbac.DeleteAction) {
return nil, fmt.Errorf("%w: cannot delete shards in %s", errors.ErrUnauthorized, req.ClusterId)
}
c, err := api.getClusterForRequest(req.ClusterId)
if err != nil {
return nil, err
}
return c.DeleteShards(ctx, req.Options)
}
// DeleteTablet is part of the vtadminpb.VTAdminServer interface.
func (api *API) DeleteTablet(ctx context.Context, req *vtadminpb.DeleteTabletRequest) (*vtadminpb.DeleteTabletResponse, error) {
span, ctx := trace.NewSpan(ctx, "API.DeleteTablet")
defer span.Finish()
tablet, c, err := api.getTabletForAction(ctx, span, rbac.DeleteAction, req.Alias, req.ClusterIds)
if err != nil {
return nil, errView on GitHub (pinned to 01a25a7d17)
Solutions
- Add 'delete' to the shard resource actions for the caller's role in the RBAC config
- Ensure the role's cluster scope includes the target cluster ID (wildcard if needed)
- Restart vtadmin so the updated RBAC policy is loaded
Example fix
// before
- resource: shard
actions: [get, create]
// after
- resource: shard
actions: [get, create, delete] Defensive patterns
Strategy: validation
Validate before calling
const canDeleteShards = permissions.some(rule => rule.resource === 'shard' && (rule.actions.includes('delete') || rule.actions.includes('*')) && (rule.clusters.includes(clusterId) || rule.clusters.includes('*')));
if (!canDeleteShards) throw new Error('RBAC denies shard delete in ' + clusterId); Type guard
function isShardDeleteDenied(err: unknown): boolean {
return err instanceof Error && err.message.includes('cannot delete shards');
} Try / catch
try {
await deleteShards(clusterId, shards);
} catch (err) {
if (String(err).includes('cannot delete shards')) {
escalateToAdmin('shard:delete grant needed for ' + clusterId);
} else {
throw err;
}
} Prevention
- Grant shard delete explicitly for decommissioning tooling
- Use wildcard cluster scope deliberately for ops roles only
- Restart vtadmin to load RBAC changes and verify with a dry-run call
When it happens
Trigger: Calling DeleteShards (DELETE /shards) for a cluster where the caller's role omits shard-delete permission.
Common situations: Decommissioning workflows run by service accounts lacking delete grants; RBAC configs granting wildcard on keyspaces but not shards; multi-cluster setups where the role covers only some cluster IDs.
Related errors
- %w: cannot create shard in %s
- %w: cannot delete keyspace in %s
- %w: cannot create schema migration in %s
- %w: cannot cancel schema migration in %s
- %w: cannot cleanup schema migration in %s
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/6e41be5e6d02a818.
Report an issue: GitHub.