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

  1. Grant the Complete action on the Workflow resource to the caller's role in the RBAC config
  2. Verify auth credentials/headers are present and map to the intended identity
  3. Confirm req.ClusterId matches the cluster covered by the RBAC rule
  4. 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

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


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