vitessio/vitess · error · errors.ErrUnauthorized
%w: cannot delete workflow in %s
Error message
%w: cannot delete workflow in %s
What it means
The WorkflowDelete RPC checks RBAC authorization before deleting a workflow. If the caller's authenticated identity lacks DeleteAction permission on the WorkflowResource for the given cluster, the request fails with errors.ErrUnauthorized wrapped with the cluster ID. This is an access-control rejection, not a workflow-state failure.
Source
Thrown at go/vt/vtadmin/api.go:2839
response, err := vte.ExplainsAsText(plans)
if err != nil {
return nil, fmt.Errorf("error converting vtexplain to text output: %w", err)
}
return &vtadminpb.VTExplainResponse{
Response: response,
}, nil
}
// WorkflowDelete is part of the vtadminpb.VTAdminServer interface.
func (api *API) WorkflowDelete(ctx context.Context, req *vtadminpb.WorkflowDeleteRequest) (*vtctldatapb.WorkflowDeleteResponse, error) {
span, ctx := trace.NewSpan(ctx, "API.WorkflowDelete")
defer span.Finish()
span.Annotate("cluster_id", req.ClusterId)
if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.WorkflowResource, rbac.DeleteAction) {
return nil, fmt.Errorf("%w: cannot delete workflow in %s", errors.ErrUnauthorized, req.ClusterId)
}
c, err := api.getClusterForRequest(req.ClusterId)
if err != nil {
return nil, err
}
// Set the default options which are not supported in VTAdmin Web.
return c.Vtctld.WorkflowDelete(ctx, req.Request)
}
// WorkflowSwitchTraffic is part of the vtadminpb.VTAdminServer interface.
func (api *API) WorkflowSwitchTraffic(ctx context.Context, req *vtadminpb.WorkflowSwitchTrafficRequest) (*vtctldatapb.WorkflowSwitchTrafficResponse, error) {
span, ctx := trace.NewSpan(ctx, "API.WorkflowSwitchTraffic")
defer span.Finish()
span.Annotate("cluster_id", req.ClusterId)
View on GitHub (pinned to 01a25a7d17)
Solutions
- Update vtadmin's RBAC rules to grant the caller's role 'delete' on the workflow resource for that cluster ID.
- Confirm req.ClusterId matches the cluster ID used in the RBAC policy (exact string match).
- Authenticate with credentials/identity that map to an authorized role (check the identity provider and vtadmin's auth setup).
- Inspect the RBAC config file (`--rbac` JSON) rules array and add e.g. {"resource": "workflow", "actions": ["delete"], "clusters": ["<clusterId>"]}.
- If you believe access should be granted, verify with `vtadmin` logs how the IsAuthorized decision was reached.
Example fix
// before: vtadmin rbac config
[{"resource": "workflow", "actions": ["get"], "clusters": ["*"]}]
// after
[{"resource": "workflow", "actions": ["get", "delete"], "clusters": ["*"]}] Defensive patterns
Strategy: try-catch
Validate before calling
// Client-side pre-check against the published RBAC policy
if !rbacPolicy.Allows(identity, rbac.WorkflowResource, rbac.DeleteAction, clusterID) {
return fmt.Errorf("identity %s may not delete workflows in cluster %s", identity, clusterID)
} Type guard
func authorized(id Identity, res rbac.Resource, act rbac.Action, cluster string) bool {
for _, r := range id.Roles {
for _, rule := range r.Rules {
if rule.Matches(res, act, cluster) { return true }
}
}
return false
} Try / catch
resp, err := api.WorkflowDelete(ctx, req)
if err != nil && strings.Contains(err.Error(), "cannot delete workflow") {
return vterrors.Errorf(vtrpcpb.Code_PERMISSION_DENIED,
"insufficient RBAC permissions to delete workflow in cluster %s", req.ClusterId)
} Prevention
- Grant the delete action on the workflow resource to the roles that need it, scoped to the correct cluster IDs.
- Hide destructive workflow actions in the UI unless the user's role allows delete.
- Keep cluster IDs in the RBAC policy in exact sync with cluster config IDs.
- Review RBAC config during role changes so delete permissions are deliberate.
When it happens
Trigger: Calling WorkflowDelete (API.GenerateWorkflowDeleteResponse) with req.ClusterId set to a cluster where the caller's RBAC rules do not grant workflow delete permission — e.g. rules limited to read actions or a different cluster ID.
Common situations: Read-only vtadmin user attempting a destructive operation; RBAC policy file grants permissions on cluster 'prod' but user targets 'staging' (or vice versa); missing 'delete' action in the role's rules; authz middleware not extracting identity, causing default-deny.
Related errors
- %w: cannot create workflow in %s
- %w: cannot create schema migration in %s
- %w: cannot cancel schema migration in %s
- %w: cannot cleanup schema migration in %s
- %w: cannot complete schema migration in %s
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/785443f2c09c706e.
Report an issue: GitHub.