{"record":{"id":"6de6a4424d0b97aa","repo":"vitessio/vitess","slug":"w-cannot-complete-schema-migration-in-s","errorCode":null,"errorMessage":"%w: cannot complete schema migration in %s","messagePattern":"%w: cannot complete schema migration in (.+?)","errorType":"validation","errorClass":"ErrUnauthorized","httpStatus":null,"severity":"error","filePath":"go/vt/vtadmin/api.go","lineNumber":568,"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.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) {\n\t\treturn nil, fmt.Errorf(\"%w: cannot complete 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.CompleteSchemaMigration(ctx, req.Request)\n}\n\n// ConcludeTransaction is part of the vtadminpb.VTAdminServer interface.\nfunc (api *API) ConcludeTransaction(ctx context.Context, req *vtadminpb.ConcludeTransactionRequest) (*vtctldatapb.ConcludeTransactionResponse, error) {\n\tspan, ctx := trace.NewSpan(ctx, \"API.ConcludeTransaction\")\n\tdefer span.Finish()\n\n\tif !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.ClusterResource, rbac.GetAction) {\n\t\treturn nil, nil\n\t}","sourceCodeStart":550,"sourceCodeEnd":586,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtadmin/api.go#L550-L586","documentation":"VTAdmin's CompleteSchemaMigration RPC returns this when RBAC denies the caller the 'complete' action on the SchemaMigration resource for the requested cluster. The authorization gate runs first and wraps errors.ErrUnauthorized. It means policy, not the migration itself, blocked the call.","triggerScenarios":"Calling CompleteSchemaMigration (POST /schema/complete) for a cluster where the caller's role does not include the complete action.","commonSituations":"Finalizing a successful VReplication-based migration with a mid-tier admin role that allows get/create but not complete; hand-written RBAC configs missing newer actions; requesting the wrong cluster ID.","solutions":["Add 'complete' to the schema-migration resource actions for the caller's role in the RBAC config","Verify the ClusterId in the request matches a role the caller holds","Restart vtadmin to pick up RBAC changes"],"exampleFix":"// before\n  - resource: schema-migration\n    actions: [get]\n// after\n  - resource: schema-migration\n    actions: [get, create, complete]","handlingStrategy":"validation","validationCode":"const canComplete = permissions.some(rule => rule.resource === 'schema-migration' && (rule.actions.includes('complete') || rule.actions.includes('*')));\nif (!canComplete) throw new Error('missing complete permission for schema-migration');","typeGuard":"function isCompleteDenied(err: unknown): boolean {\n  return err instanceof Error && err.message.includes('cannot complete schema migration');\n}","tryCatchPattern":"try {\n  await completeSchemaMigration(clusterId, uuid);\n} catch (err) {\n  if (String(err).includes('cannot complete schema migration')) {\n    requestGrant('schema-migration:complete', clusterId);\n  } else {\n    throw err;\n  }\n}","preventionTips":["Grant the full migration lifecycle in one role to avoid partial-permission dead ends","Confirm the migration workflow's service account has complete rights in every target cluster","Re-authenticate after role updates"],"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"}