{"record":{"id":"260d871fbd5fc91b","repo":"vitessio/vitess","slug":"w-cannot-create-shard-in-s","errorCode":null,"errorMessage":"%w: cannot create shard in %s","messagePattern":"%w: cannot create shard in (.+?)","errorType":"validation","errorClass":"ErrUnauthorized","httpStatus":null,"severity":"error","filePath":"go/vt/vtadmin/api.go","lineNumber":634,"sourceCode":"\tks, err := c.CreateKeyspace(ctx, req.Options)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &vtadminpb.CreateKeyspaceResponse{\n\t\tKeyspace: ks,\n\t}, nil\n}\n\n// CreateShard is part of the vtadminpb.VTAdminServer interface.\nfunc (api *API) CreateShard(ctx context.Context, req *vtadminpb.CreateShardRequest) (*vtctldatapb.CreateShardResponse, error) {\n\tspan, ctx := trace.NewSpan(ctx, \"API.CreateShard\")\n\tdefer span.Finish()\n\n\tspan.Annotate(\"cluster_id\", req.ClusterId)\n\n\tif !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.ShardResource, rbac.CreateAction) {\n\t\treturn nil, fmt.Errorf(\"%w: cannot create shard in %s\", errors.ErrUnauthorized, req.ClusterId)\n\t}\n\n\tc, err := api.getClusterForRequest(req.ClusterId)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn c.CreateShard(ctx, req.Options)\n}\n\n// DeleteKeyspace is part of the vtadminpb.VTAdminServer interface.\nfunc (api *API) DeleteKeyspace(ctx context.Context, req *vtadminpb.DeleteKeyspaceRequest) (*vtctldatapb.DeleteKeyspaceResponse, error) {\n\tspan, ctx := trace.NewSpan(ctx, \"API.DeleteKeyspace\")\n\tdefer span.Finish()\n\n\tspan.Annotate(\"cluster_id\", req.ClusterId)\n\n\tif !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.KeyspaceResource, rbac.DeleteAction) {","sourceCodeStart":616,"sourceCodeEnd":652,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtadmin/api.go#L616-L652","documentation":"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.","triggerScenarios":"Calling CreateShard (POST /shard) for a cluster whose rules do not grant the caller shard-create permission.","commonSituations":"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.","solutions":["Add action 'create' to the shard resource rules for the caller's role in the RBAC config","Confirm the request targets a cluster ID covered by that role","Reload vtadmin with the updated config"],"exampleFix":"// before\n  - resource: shard\n    actions: [get]\n// after\n  - resource: shard\n    actions: [get, create, delete]","handlingStrategy":"validation","validationCode":"const canCreateShard = permissions.some(rule => rule.resource === 'shard' && (rule.actions.includes('create') || rule.actions.includes('*')) && (rule.clusters.includes(clusterId) || rule.clusters.includes('*')));\nif (!canCreateShard) throw new Error('RBAC denies shard create in ' + clusterId);","typeGuard":"function isShardCreateDenied(err: unknown): boolean {\n  return err instanceof Error && err.message.includes('cannot create shard');\n}","tryCatchPattern":"try {\n  await createShard(clusterId, shardReq);\n} catch (err) {\n  if (String(err).includes('cannot create shard')) {\n    logRbacDenial('shard:create', clusterId);\n  } else {\n    throw err;\n  }\n}","preventionTips":["Ensure the RBAC config defines the shard resource explicitly (missing resource = default deny)","Pair keyspace and shard permissions when provisioning workflows do both","Verify cluster scoping per role"],"tags":["rbac","authorization","vtadmin","shard"],"backgroundTag":"unauthorized-rbac-action","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}