vitessio/vitess · error · ErrUnauthorized

%w: cannot retry schema migration in %s

Error message

%w: cannot retry schema migration in %s

What it means

VTAdmin's RetrySchemaMigration RPC returns this when RBAC denies the retry action on the schema_migration resource in the target cluster. It wraps errors.ErrUnauthorized for errors.Is matching. It is returned before contacting vtctld.

Source

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

	})
	if err != nil {
		return nil, err
	}

	return &vtadminpb.ReloadSchemaShardResponse{
		Events: res.Events,
	}, nil
}

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

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

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

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

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

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

	tablet, c, err := api.getTabletForAction(ctx, span, rbac.GetAction, req.Alias, req.ClusterIds)
	if err != nil {
		return nil, err

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Grant the retry action on the schema_migration resource to the caller's RBAC role
  2. Confirm the cluster_id in the request matches the rules configured for that role
  3. Verify authenticated identity resolution (auth headers/Impersonation rules) maps to the intended subject

Example fix

// before
- resource: schema_migration
  actions: [get]
// after
- resource: schema_migration
  actions: [get, retry]
Defensive patterns

Strategy: validation

Validate before calling

func canRetrySchemaMigration(role string) bool {
    allowed := map[string]bool{"admin": true, "operator": true}
    return allowed[role]
}

Type guard

func isUnauthorizedErr(err error) bool {
    return err != nil && errors.Is(err, apierrors.ErrUnauthorized)
}

Try / catch

resp, err := client.RetrySchemaMigration(ctx, req)
if err != nil {
    if errors.Is(err, apierrors.ErrUnauthorized) {
        return fmt.Errorf("insufficient RBAC permissions to retry schema migration in %s", req.ClusterId)
    }
    return err
}

Prevention

When it happens

Trigger: Calling API.RetrySchemaMigration when api.authz.IsAuthorized(ctx, req.ClusterId, rbac.SchemaMigrationResource, rbac.RetryAction) returns false.

Common situations: RBAC role has get/list on schema migrations but not retry; users trying to retry a failed migration with a viewer role; misconfigured subject mapping falling back to a restricted role.

Related errors


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