vitessio/vitess · error · ErrUnauthorized

%w: cannot create shard in %s

Error message

%w: cannot create shard in %s

What it means

VTAdmin's CreateShard RPC returns this when RBAC denies the caller the 'create' action on the Shard resource for the requested cluster. The API enforces the check before resolving the cluster and wraps errors.ErrUnauthorized. It signals a permissions problem, not a shard conflict.

Source

Thrown at go/vt/vtadmin/api.go:634

	ks, err := c.CreateKeyspace(ctx, req.Options)
	if err != nil {
		return nil, err
	}

	return &vtadminpb.CreateKeyspaceResponse{
		Keyspace: ks,
	}, nil
}

// CreateShard is part of the vtadminpb.VTAdminServer interface.
func (api *API) CreateShard(ctx context.Context, req *vtadminpb.CreateShardRequest) (*vtctldatapb.CreateShardResponse, error) {
	span, ctx := trace.NewSpan(ctx, "API.CreateShard")
	defer span.Finish()

	span.Annotate("cluster_id", req.ClusterId)

	if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.ShardResource, rbac.CreateAction) {
		return nil, fmt.Errorf("%w: cannot create shard in %s", errors.ErrUnauthorized, req.ClusterId)
	}

	c, err := api.getClusterForRequest(req.ClusterId)
	if err != nil {
		return nil, err
	}

	return c.CreateShard(ctx, req.Options)
}

// DeleteKeyspace is part of the vtadminpb.VTAdminServer interface.
func (api *API) DeleteKeyspace(ctx context.Context, req *vtadminpb.DeleteKeyspaceRequest) (*vtctldatapb.DeleteKeyspaceResponse, error) {
	span, ctx := trace.NewSpan(ctx, "API.DeleteKeyspace")
	defer span.Finish()

	span.Annotate("cluster_id", req.ClusterId)

	if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.KeyspaceResource, rbac.DeleteAction) {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Add action 'create' to the shard resource rules for the caller's role in the RBAC config
  2. Confirm the request targets a cluster ID covered by that role
  3. Reload vtadmin with the updated config

Example fix

// before
  - resource: shard
    actions: [get]
// after
  - resource: shard
    actions: [get, create, delete]
Defensive patterns

Strategy: validation

Validate before calling

const canCreateShard = permissions.some(rule => rule.resource === 'shard' && (rule.actions.includes('create') || rule.actions.includes('*')) && (rule.clusters.includes(clusterId) || rule.clusters.includes('*')));
if (!canCreateShard) throw new Error('RBAC denies shard create in ' + clusterId);

Type guard

function isShardCreateDenied(err: unknown): boolean {
  return err instanceof Error && err.message.includes('cannot create shard');
}

Try / catch

try {
  await createShard(clusterId, shardReq);
} catch (err) {
  if (String(err).includes('cannot create shard')) {
    logRbacDenial('shard:create', clusterId);
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling CreateShard (POST /shard) for a cluster whose rules do not grant the caller shard-create permission.

Common situations: Operators allowed to create keyspaces but not shards (resource-scoped RBAC); missing shard resource block entirely in rbac config so default-deny applies; wrong cluster ID in the request.

Related errors


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