vitessio/vitess · error · ErrUnauthorized
%w: cannot complete workflow in %s
Error message
%w: cannot complete workflow in %s
What it means
API.MoveTablesComplete requires the Complete action on WorkflowResource in the target cluster; unauthorized callers receive errors.ErrUnauthorized wrapped with this message before any vtctld call is made. The sentinel wrap allows programmatic detection via errors.Is(err, errors.ErrUnauthorized).
Source
Thrown at go/vt/vtadmin/api.go:2065
return nil, err
}
req.Request.Settings.TableSettings, err = vreplcommon.ParseTableMaterializeSettings(req.TableSettings, parser)
if err != nil {
return nil, err
}
return c.Vtctld.MaterializeCreate(ctx, req.Request)
}
// MoveTablesComplete is part of the vtadminpb.VTAdminServer interface.
func (api *API) MoveTablesComplete(ctx context.Context, req *vtadminpb.MoveTablesCompleteRequest) (*vtctldatapb.MoveTablesCompleteResponse, error) {
span, ctx := trace.NewSpan(ctx, "API.MoveTablesComplete")
defer span.Finish()
span.Annotate("cluster_id", req.ClusterId)
if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.WorkflowResource, rbac.CompleteAction) {
return nil, fmt.Errorf("%w: cannot complete workflow in %s", errors.ErrUnauthorized, req.ClusterId)
}
c, err := api.getClusterForRequest(req.ClusterId)
if err != nil {
return nil, err
}
return c.Vtctld.MoveTablesComplete(ctx, req.Request)
}
// MoveTablesCreate is part of the vtadminpb.VTAdminServer interface.
func (api *API) MoveTablesCreate(ctx context.Context, req *vtadminpb.MoveTablesCreateRequest) (*vtctldatapb.WorkflowStatusResponse, error) {
span, ctx := trace.NewSpan(ctx, "API.MoveTablesCreate")
defer span.Finish()
span.Annotate("cluster_id", req.ClusterId)
if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.WorkflowResource, rbac.CreateAction) {View on GitHub (pinned to 01a25a7d17)
Solutions
- Grant the Complete action on the Workflow resource to the caller's role in the RBAC config
- Verify auth credentials/headers are present and map to the intended identity
- Confirm req.ClusterId matches the cluster covered by the RBAC rule
- Restart vtadmin to pick up edited authz configuration
Example fix
// rbac.yaml before - resource: "Workflow" actions: ["get", "create"] // after - resource: "Workflow" actions: ["get", "create", "complete"]
Defensive patterns
Strategy: validation
Validate before calling
// pre-check workflow complete permission
if !userRoles.Can("complete", "Workflow", req.ClusterId) {
return fmt.Errorf("user cannot complete workflows in %s", req.ClusterId)
} Type guard
func isUnauthorized(err error) bool {
return errors.Is(err, vtadminerrors.ErrUnauthorized)
} Try / catch
resp, err := client.MoveTablesComplete(ctx, req)
if err != nil {
if isUnauthorized(err) {
// request RBAC change; do not retry as-is
}
return err
} Prevention
- Grant Workflow complete action for move-tables operators
- Confirm request cluster IDs match RBAC rule scopes
- Check identity mapping when authz unexpectedly denies
- Document least-privilege roles for workflow lifecycle actions
When it happens
Trigger: Calling MoveTablesComplete (completing a MoveTables workflow) with credentials lacking workflow complete permission in req.ClusterId — read-only roles, cluster scope mismatch, or missing auth credentials.
Common situations: Users allowed to create workflows but not complete them (action not granted); cluster ID typo hitting a cluster whose rules exclude the caller; proxies dropping auth headers; stale RBAC config after team permission changes.
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 complete schema migration in %s
- %w: cannot create keyspace in %s
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/9be333baa7e85643.
Report an issue: GitHub.