{"record":{"id":"5a8cad776ff4da8e","repo":"vitessio/vitess","slug":"w-cannot-cleanup-schema-migration-in-s","errorCode":null,"errorMessage":"%w: cannot cleanup schema migration in %s","messagePattern":"%w: cannot cleanup schema migration in (.+?)","errorType":"validation","errorClass":"ErrUnauthorized","httpStatus":null,"severity":"error","filePath":"go/vt/vtadmin/api.go","lineNumber":549,"sourceCode":"\t}\n\n\tc, err := api.getClusterForRequest(req.ClusterId)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn c.CancelSchemaMigration(ctx, req.Request)\n}\n\n// CleanupSchemaMigration is part of the vtadminpb.VTAdminServer interface.\nfunc (api *API) CleanupSchemaMigration(ctx context.Context, req *vtadminpb.CleanupSchemaMigrationRequest) (*vtctldatapb.CleanupSchemaMigrationResponse, error) {\n\tspan, ctx := trace.NewSpan(ctx, \"API.CleanupSchemaMigration\")\n\tdefer span.Finish()\n\n\tspan.Annotate(\"cluster_id\", req.ClusterId)\n\n\tif !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.SchemaMigrationResource, rbac.CleanupSchemaMigrationAction) {\n\t\treturn nil, fmt.Errorf(\"%w: cannot cleanup schema migration 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.CleanupSchemaMigration(ctx, req.Request)\n}\n\n// CompleteSchemaMigration is part of the vtadminpb.VTAdminServer interface.\nfunc (api *API) CompleteSchemaMigration(ctx context.Context, req *vtadminpb.CompleteSchemaMigrationRequest) (*vtctldatapb.CompleteSchemaMigrationResponse, error) {\n\tspan, ctx := trace.NewSpan(ctx, \"API.CompleteSchemaMigration\")\n\tdefer span.Finish()\n\n\tspan.Annotate(\"cluster_id\", req.ClusterId)\n\n\tif !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.SchemaMigrationResource, rbac.CompleteSchemaMigrationAction) {","sourceCodeStart":531,"sourceCodeEnd":567,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtadmin/api.go#L531-L567","documentation":"VTAdmin's CleanupSchemaMigration RPC returns this when the caller's role lacks the 'cleanup' action on the SchemaMigration resource for the target cluster. The API rejects the request before contacting any cluster, wrapping errors.ErrUnauthorized. This is a deliberate RBAC denial.","triggerScenarios":"Calling CleanupSchemaMigration (POST /schema/cleanup) against a cluster whose RBAC rules do not grant the caller the cleanup action.","commonSituations":"Attempting to clean up a completed/failed migration with a role that only allows read or create; copy-pasted RBAC rules that miss the newer cleanup/complete/cancel actions; cluster-scoped rules that don't match the request's ClusterId.","solutions":["Grant the 'cleanup' action on the schema-migration resource to the caller's role in the RBAC config","Check that the cluster ID in the request is covered by the intended role (add wildcard if appropriate)","Apply the config change and restart vtadmin"],"exampleFix":"// before\n  - resource: schema-migration\n    actions: [get, create, cancel]\n// after\n  - resource: schema-migration\n    actions: [get, create, cancel, cleanup]","handlingStrategy":"validation","validationCode":"const canCleanup = permissions.some(rule => rule.resource === 'schema-migration' && (rule.actions.includes('cleanup') || rule.actions.includes('*')));\nif (!canCleanup) throw new Error('missing cleanup permission for schema-migration');","typeGuard":"function isCleanupDenied(err: unknown): boolean {\n  return err instanceof Error && err.message.includes('cannot cleanup schema migration');\n}","tryCatchPattern":"try {\n  await cleanupSchemaMigration(clusterId, uuid);\n} catch (err) {\n  if (String(err).includes('cannot cleanup schema migration')) {\n    escalateToAdmin('cleanup grant needed for ' + clusterId);\n  } else {\n    throw err;\n  }\n}","preventionTips":["Audit RBAC actions for schema-migration lifecycle ops (create/cancel/complete/cleanup) as a set","Keep cluster IDs consistent between request and role scope","Restart vtadmin after RBAC edits"],"tags":["rbac","authorization","vtadmin","schema-migration"],"backgroundTag":"unauthorized-rbac-action","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}