vitessio/vitess · error · ErrUnauthorized

%w: cannot cancel schema migration in %s

Error message

%w: cannot cancel schema migration in %s

What it means

VTAdmin's CancelSchemaMigration RPC returns this when the caller is not authorized with the 'cancel' action on the SchemaMigration resource for the given cluster. The check runs before any cluster lookup, wrapping errors.ErrUnauthorized. It indicates RBAC policy denial, not a migration failure.

Source

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

	if req.CallerId != "" {
		req.Request.CallerId = &vtrpcpb.CallerID{Principal: req.CallerId}
	}

	// Set the default wait replicas timeout.
	req.Request.WaitReplicasTimeout = protoutil.DurationToProto(grpcvtctldserver.DefaultWaitReplicasTimeout)

	return c.ApplySchema(ctx, req.Request)
}

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

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

	if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.SchemaMigrationResource, rbac.CancelAction) {
		return nil, fmt.Errorf("%w: cannot cancel schema migration in %s", errors.ErrUnauthorized, req.ClusterId)
	}

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

	return c.CancelSchemaMigration(ctx, req.Request)
}

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

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

	if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.SchemaMigrationResource, rbac.CleanupSchemaMigrationAction) {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Add action 'cancel' (or use a wildcard) to the caller's role for the schema-migration resource in the RBAC config
  2. Confirm the request's cluster ID is the one the permissive role covers
  3. Reload/restart vtadmin with the updated RBAC config

Example fix

// before
  - resource: schema-migration
    actions: [get, create]
// after
  - resource: schema-migration
    actions: [get, create, cancel, complete, cleanup]
Defensive patterns

Strategy: validation

Validate before calling

const canCancel = permissions.some(rule => rule.resource === 'schema-migration' && (rule.actions.includes('cancel') || rule.actions.includes('*')) && (rule.clusters.includes(clusterId) || rule.clusters.includes('*')));
if (!canCancel) console.warn('RBAC will deny CancelSchemaMigration for cluster ' + clusterId);

Type guard

function isCancelUnauthorized(err: unknown): boolean {
  return err instanceof Error && err.message.includes('cannot cancel schema migration');
}

Try / catch

try {
  await cancelSchemaMigration(clusterId, uuid);
} catch (err) {
  if (String(err).includes('cannot cancel schema migration')) {
    requestRbacGrant(['cancel']);
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling CancelSchemaMigration (DELETE /schema/cancel) for a cluster where the caller's role omits the cancel action on schema-migration resources.

Common situations: Operators with create-only schema permissions trying to cancel an in-flight migration; RBAC config that enumerates actions narrowly (get, create) but not cancel; wrong cluster ID mapped to a restrictive role.

Related errors


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