vitessio/vitess · error · ErrUnauthorized

%w: cannot cleanup schema migration in %s

Error message

%w: cannot cleanup schema migration in %s

What it means

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.

Source

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

	}

	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) {
		return nil, fmt.Errorf("%w: cannot cleanup schema migration in %s", errors.ErrUnauthorized, req.ClusterId)
	}

	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) {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Grant the 'cleanup' action on the schema-migration resource to the caller's role in the RBAC config
  2. Check that the cluster ID in the request is covered by the intended role (add wildcard if appropriate)
  3. Apply the config change and restart vtadmin

Example fix

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

Strategy: validation

Validate before calling

const canCleanup = permissions.some(rule => rule.resource === 'schema-migration' && (rule.actions.includes('cleanup') || rule.actions.includes('*')));
if (!canCleanup) throw new Error('missing cleanup permission for schema-migration');

Type guard

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

Try / catch

try {
  await cleanupSchemaMigration(clusterId, uuid);
} catch (err) {
  if (String(err).includes('cannot cleanup schema migration')) {
    escalateToAdmin('cleanup grant needed for ' + clusterId);
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling CleanupSchemaMigration (POST /schema/cleanup) against a cluster whose RBAC rules do not grant the caller the cleanup action.

Common situations: 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.

Related errors


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