argoproj/argo-workflows · error

invalid spec: clusterworkflowtemplates.argoproj.io `%s` is f

Error message

invalid spec: clusterworkflowtemplates.argoproj.io `%s` is forbidden: User cannot get resource 'clusterworkflowtemplates' in API group argoproj.io at the cluster scope

What it means

When a workflow references a template from a ClusterWorkflowTemplate, the controller uses a cluster-scoped clientset. If the ClusterWorkflowTemplateGetter is the Null implementation (no cluster template support configured/authorized), Get always returns this fixed RBAC-forbidden error so template resolution fails with a k8s-style forbidden message.

Source

Thrown at workflow/templateresolution/context.go:56

// clusterWorkflowTemplateInterfaceWrapper is an internal struct to wrap clientset.
type clusterWorkflowTemplateInterfaceWrapper struct {
	clientset typed.ClusterWorkflowTemplateInterface
}

// ClusterWorkflowTemplateGetter helps get WorkflowTemplates.
type ClusterWorkflowTemplateGetter interface {
	// Get retrieves the WorkflowTemplate from the indexer for a given name.
	Get(ctx context.Context, name string) (*wfv1.ClusterWorkflowTemplate, error)
}

func WrapClusterWorkflowTemplateInterface(clusterClientset typed.ClusterWorkflowTemplateInterface) ClusterWorkflowTemplateGetter {
	return &clusterWorkflowTemplateInterfaceWrapper{clientset: clusterClientset}
}

type NullClusterWorkflowTemplateGetter struct{}

func (n *NullClusterWorkflowTemplateGetter) Get(_ context.Context, name string) (*wfv1.ClusterWorkflowTemplate, error) {
	return nil, errors.Errorf("", "invalid spec: clusterworkflowtemplates.argoproj.io `%s` is "+
		"forbidden: User cannot get resource 'clusterworkflowtemplates' in API group argoproj.io at the cluster scope", name)
}

// Get retrieves the WorkflowTemplate of a given name.
func (wrapper *clusterWorkflowTemplateInterfaceWrapper) Get(ctx context.Context, name string) (*wfv1.ClusterWorkflowTemplate, error) {
	return wrapper.clientset.Get(ctx, name, metav1.GetOptions{})
}

// TemplateContext is a context of template search.
type TemplateContext struct {
	// wftmplGetter is an interface to get WorkflowTemplates.
	wftmplGetter WorkflowTemplateNamespacedGetter
	// cwftmplGetter is an interface to get ClusterWorkflowTemplates
	cwftmplGetter ClusterWorkflowTemplateGetter
	// tmplBase is the base of local template search.
	tmplBase wfv1.TemplateHolder
	// workflow is the Workflow where templates will be stored
	workflow *wfv1.Workflow

View on GitHub (pinned to 35bff19146)

Solutions

  1. Grant the caller/controller RBAC get on clusterworkflowtemplates.argoproj.io at cluster scope
  2. Remove clusterScope: true and use a namespace-local WorkflowTemplate
  3. Deploy the ClusterWorkflowTemplate CRD / enable cluster template support in the controller

Example fix

// before
templateRef:
  name: my-cluster-template
  template: step
  clusterScope: true
// after
templateRef:
  name: my-template
  template: step
  clusterScope: false
Defensive patterns

Strategy: fallback

Validate before calling

// check RBAC before submitting a clusterScope templateRef
kubectl auth can-i get clusterworkflowtemplates.argoproj.io --as=<user>

Try / catch

tpl, err := tplCtx.GetTemplateByName(ctx, name)
if err != nil && strings.Contains(err.Error(), "forbidden") {
  // fall back to namespaced WorkflowTemplate or report RBAC misconfiguration
}

Prevention

When it happens

Trigger: templateRef with clusterScope: true while the installation has no ClusterWorkflowTemplate CRD support or the caller lacks RBAC to get clusterworkflowtemplates at cluster scope.

Common situations: Namespaced-only argo deployments (cluster workflow templates feature disabled); RBAC policies blocking cluster-scoped reads for the controller or the submitting user via SSO.

Understand the failure class

Related errors


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/3c1ebe10f179fb86. Report an issue: GitHub.