{"record":{"id":"9be333baa7e85643","repo":"vitessio/vitess","slug":"w-cannot-complete-workflow-in-s","errorCode":null,"errorMessage":"%w: cannot complete workflow in %s","messagePattern":"%w: cannot complete workflow in (.+?)","errorType":"validation","errorClass":"ErrUnauthorized","httpStatus":null,"severity":"error","filePath":"go/vt/vtadmin/api.go","lineNumber":2065,"sourceCode":"\t\treturn nil, err\n\t}\n\treq.Request.Settings.TableSettings, err = vreplcommon.ParseTableMaterializeSettings(req.TableSettings, parser)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn c.Vtctld.MaterializeCreate(ctx, req.Request)\n}\n\n// MoveTablesComplete is part of the vtadminpb.VTAdminServer interface.\nfunc (api *API) MoveTablesComplete(ctx context.Context, req *vtadminpb.MoveTablesCompleteRequest) (*vtctldatapb.MoveTablesCompleteResponse, error) {\n\tspan, ctx := trace.NewSpan(ctx, \"API.MoveTablesComplete\")\n\tdefer span.Finish()\n\n\tspan.Annotate(\"cluster_id\", req.ClusterId)\n\n\tif !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.WorkflowResource, rbac.CompleteAction) {\n\t\treturn nil, fmt.Errorf(\"%w: cannot complete workflow in %s\", errors.ErrUnauthorized, req.ClusterId)\n\t}\n\n\tc, err := api.getClusterForRequest(req.ClusterId)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn c.Vtctld.MoveTablesComplete(ctx, req.Request)\n}\n\n// MoveTablesCreate is part of the vtadminpb.VTAdminServer interface.\nfunc (api *API) MoveTablesCreate(ctx context.Context, req *vtadminpb.MoveTablesCreateRequest) (*vtctldatapb.WorkflowStatusResponse, error) {\n\tspan, ctx := trace.NewSpan(ctx, \"API.MoveTablesCreate\")\n\tdefer span.Finish()\n\n\tspan.Annotate(\"cluster_id\", req.ClusterId)\n\n\tif !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.WorkflowResource, rbac.CreateAction) {","sourceCodeStart":2047,"sourceCodeEnd":2083,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtadmin/api.go#L2047-L2083","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// rbac.yaml before\n- resource: \"Workflow\"\n  actions: [\"get\", \"create\"]\n// after\n- resource: \"Workflow\"\n  actions: [\"get\", \"create\", \"complete\"]","handlingStrategy":"validation","validationCode":"// pre-check workflow complete permission\nif !userRoles.Can(\"complete\", \"Workflow\", req.ClusterId) {\n\treturn fmt.Errorf(\"user cannot complete workflows in %s\", req.ClusterId)\n}","typeGuard":"func isUnauthorized(err error) bool {\n\treturn errors.Is(err, vtadminerrors.ErrUnauthorized)\n}","tryCatchPattern":"resp, err := client.MoveTablesComplete(ctx, req)\nif err != nil {\n\tif isUnauthorized(err) {\n\t\t// request RBAC change; do not retry as-is\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["vtadmin","rbac","authorization","movetables"],"backgroundTag":"rbac-unauthorized","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}