argoproj/argo-workflows · error
Forbidden
Error message
Forbidden
What it means
Before uploading, the server checks RBAC with auth.CanI for verb 'get' on workflowtemplates in the target namespace for the named template. If the caller's credentials (client token, server SA, or SSO claims) lack that permission, the handler responds 403 Forbidden. This enforces least-privilege: you may only create workflows from templates you can read.
Source
Thrown at server/artifacts/artifact_server.go:119
a.unauthorizedError(w)
return
}
a.logger.WithFields(logging.Fields{
"namespace": namespace,
"workflowTemplateName": workflowTemplateName,
"artifactName": artifactName,
}).Info(ctx, "Upload artifact")
// Authorize before reading the request body, so an unprivileged caller
// cannot force the server to buffer a large upload just to be rejected.
allowed, err := auth.CanI(ctx, "get", "workflowtemplates", namespace, workflowTemplateName)
if err != nil {
a.serverInternalError(ctx, err, w)
return
}
if !allowed {
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
return
}
allowed, err = auth.CanI(ctx, "create", "workflows", namespace, "")
if err != nil {
a.serverInternalError(ctx, err, w)
return
}
if !allowed {
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
return
}
// Get WorkflowTemplate to find artifact configuration
wfClient := auth.GetWfClient(ctx)
wfTemplate, err := wfClient.ArgoprojV1alpha1().WorkflowTemplates(namespace).Get(ctx, workflowTemplateName, metav1.GetOptions{})
if err != nil {
http.Error(w, fmt.Sprintf("Failed to get WorkflowTemplate %s/%s: %v", namespace, workflowTemplateName, err), http.StatusNotFound)
returnView on GitHub (pinned to 35bff19146)
Solutions
- Grant the caller's Role/ClusterRole a rule: apiGroups: [argoproj.io], resources: [workflowtemplates], verbs: [get] in that namespace
- Bind that role to the user/service account making the request
- Verify the namespace in the URL matches the namespace where RBAC was granted
- Check SSO RBAC label mapping if using SSO auth mode
Example fix
# before (Role without workflowtemplates) rules: - apiGroups: [argoproj.io] resources: [workflows] verbs: [create] # after rules: - apiGroups: [argoproj.io] resources: [workflows] verbs: [create] - apiGroups: [argoproj.io] resources: [workflowtemplates] verbs: [get]
Defensive patterns
Strategy: validation
Validate before calling
kubectl auth can-i get workflowtemplates -n <namespace> --as=<user> # must be yes kubectl auth can-i create workflows -n <namespace> --as=<user> # must be yes
Try / catch
if resp.StatusCode == http.StatusForbidden {
return fmt.Errorf("caller lacks RBAC (workflowtemplates/get or workflows/create) in %s", namespace)
} Prevention
- Use `kubectl auth can-i` to preflight both permissions before upload
- Create a dedicated role for artifact upload bundling both verbs
- For SSO, verify claim-to-serviceaccount RBAC mapping includes these rules
- Double-check the namespace in the URL matches where RBAC was granted
When it happens
Trigger: Submitting an upload with a token whose RBAC role has no argoproj.io/workflowtemplates get permission in the target namespace; SSO user not mapped to a service account with that role.
Common situations: CI tokens scoped to workflows only; SSO RBAC mapping missing the workflowtemplates rule; wrong namespace used in the URL (permissions are namespace-scoped).
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- invalid spec: clusterworkflowtemplates.argoproj.io `%s` is f
- failed to get existing cluster workflow template %q to updat
- failed to list SSO RBAC service accounts: %w
- no service account rule matches
- failed to get service account secret: %w
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/04617c7e83d37b6a.
Report an issue: GitHub.