vitessio/vitess · error · ErrUnauthorized
%w: cannot create workflow in %s
Error message
%w: cannot create workflow in %s
What it means
API.MaterializeCreate checks RBAC before creating a materialize workflow: callers not authorized for the Create action on WorkflowResource in req.ClusterId get errors.ErrUnauthorized wrapped with this message. The check happens before cluster resolution and any vtctld interaction, so authorization failure always yields this specific error.
Source
Thrown at go/vt/vtadmin/api.go:2033
}
c, err := api.getClusterForRequest(req.ClusterId)
if err != nil {
return nil, err
}
return c.LaunchSchemaMigration(ctx, req.Request)
}
// MaterializeCreate is part of the vtadminpb.VTAdminServer interface.
func (api *API) MaterializeCreate(ctx context.Context, req *vtadminpb.MaterializeCreateRequest) (*vtctldatapb.MaterializeCreateResponse, error) {
span, ctx := trace.NewSpan(ctx, "API.MaterializeCreate")
defer span.Finish()
span.Annotate("cluster_id", req.ClusterId)
if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.WorkflowResource, rbac.CreateAction) {
return nil, fmt.Errorf("%w: cannot create workflow in %s", errors.ErrUnauthorized, req.ClusterId)
}
c, err := api.getClusterForRequest(req.ClusterId)
if err != nil {
return nil, err
}
// Parser with default options. New() itself initializes with default MySQL version.
parser, err := sqlparser.New(sqlparser.Options{
TruncateUILen: 512,
TruncateErrLen: 0,
})
if err != nil {
return nil, err
}
req.Request.Settings.TableSettings, err = vreplcommon.ParseTableMaterializeSettings(req.TableSettings, parser)
if err != nil {
return nil, errView on GitHub (pinned to 01a25a7d17)
Solutions
- Add/grant the Create action on the Workflow resource for the caller's role in the RBAC configuration
- Confirm the client sends the identity headers/token vtadmin expects
- Check the cluster ID matches the cluster-scoped RBAC rule
- Validate the authz config (action names, cluster patterns) and restart vtadmin
Example fix
// rbac.yaml before - resource: "Workflow" actions: ["get"] // after - resource: "Workflow" actions: ["get", "create"]
Defensive patterns
Strategy: validation
Validate before calling
// pre-check workflow create permission
if !userRoles.Can("create", "Workflow", req.ClusterId) {
return fmt.Errorf("user cannot create workflows in %s", req.ClusterId)
} Type guard
func isUnauthorized(err error) bool {
return errors.Is(err, vtadminerrors.ErrUnauthorized)
} Try / catch
resp, err := client.MaterializeCreate(ctx, req)
if err != nil {
if isUnauthorized(err) {
// prompt for elevated role / correct cluster; no retry
}
return err
} Prevention
- Grant Workflow create action to roles that need materializations
- Verify proxies don't strip vtadmin auth headers
- Audit RBAC YAML for action-name typos
- Reload authz config after edits (restart vtadmin)
When it happens
Trigger: Calling MaterializeCreate (materialization workflow creation) with credentials whose RBAC rules do not grant workflow create in the target cluster — missing role mapping, cluster-scoped rule excluding this cluster, or unauthenticated request.
Common situations: Operator accounts with read-only RBAC attempting workflow creation; new cluster added without extending workflow RBAC rules; token/headers stripped by a reverse proxy; authz YAML typo in action names.
Related errors
- %w: cannot delete 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/75e8a98ac3cbcb33.
Report an issue: GitHub.