vitessio/vitess · error · ErrUnauthorized
%w: cannot complete schema migration in %s
Error message
%w: cannot complete schema migration in %s
What it means
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.
Source
Thrown at go/vt/vtadmin/api.go:568
}
c, err := api.getClusterForRequest(req.ClusterId)
if err != nil {
return nil, err
}
return c.CleanupSchemaMigration(ctx, req.Request)
}
// CompleteSchemaMigration is part of the vtadminpb.VTAdminServer interface.
func (api *API) CompleteSchemaMigration(ctx context.Context, req *vtadminpb.CompleteSchemaMigrationRequest) (*vtctldatapb.CompleteSchemaMigrationResponse, error) {
span, ctx := trace.NewSpan(ctx, "API.CompleteSchemaMigration")
defer span.Finish()
span.Annotate("cluster_id", req.ClusterId)
if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.SchemaMigrationResource, rbac.CompleteSchemaMigrationAction) {
return nil, fmt.Errorf("%w: cannot complete schema migration in %s", errors.ErrUnauthorized, req.ClusterId)
}
c, err := api.getClusterForRequest(req.ClusterId)
if err != nil {
return nil, err
}
return c.CompleteSchemaMigration(ctx, req.Request)
}
// ConcludeTransaction is part of the vtadminpb.VTAdminServer interface.
func (api *API) ConcludeTransaction(ctx context.Context, req *vtadminpb.ConcludeTransactionRequest) (*vtctldatapb.ConcludeTransactionResponse, error) {
span, ctx := trace.NewSpan(ctx, "API.ConcludeTransaction")
defer span.Finish()
if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.ClusterResource, rbac.GetAction) {
return nil, nil
}View on GitHub (pinned to 01a25a7d17)
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
Example fix
// before
- resource: schema-migration
actions: [get]
// after
- resource: schema-migration
actions: [get, create, complete] Defensive patterns
Strategy: validation
Validate before calling
const canComplete = permissions.some(rule => rule.resource === 'schema-migration' && (rule.actions.includes('complete') || rule.actions.includes('*')));
if (!canComplete) throw new Error('missing complete permission for schema-migration'); Type guard
function isCompleteDenied(err: unknown): boolean {
return err instanceof Error && err.message.includes('cannot complete schema migration');
} Try / catch
try {
await completeSchemaMigration(clusterId, uuid);
} catch (err) {
if (String(err).includes('cannot complete schema migration')) {
requestGrant('schema-migration:complete', clusterId);
} else {
throw err;
}
} Prevention
- 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
When it happens
Trigger: Calling CompleteSchemaMigration (POST /schema/complete) for a cluster where the caller's role does not include the complete action.
Common situations: 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.
Related errors
- %w: cannot create schema migration in %s
- %w: cannot cancel schema migration in %s
- %w: cannot cleanup schema migration in %s
- %w: cannot launch schema migration in %s
- %w: cannot retry schema migration in %s
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/6de6a4424d0b97aa.
Report an issue: GitHub.