argoproj/argo-workflows · error

cannot fetch workflow spec without workflowTemplateRef

Error message

cannot fetch workflow spec without workflowTemplateRef

What it means

The controller's fetchWorkflowSpec only supports workflows whose spec uses workflowTemplateRef to source their spec from a WorkflowTemplate. If a workflow has an inline spec (no workflowTemplateRef), the controller cannot retrieve a spec holder and returns this error, aborting reconciliation of that workflow.

Source

Thrown at workflow/controller/operator.go:4451

		return false, nil
	}

	parentTemplate, templateStored, err := woc.GetTemplateByBoundaryID(ctx, boundaryID)
	if err != nil || parentTemplate == nil {
		return false, err
	}
	// A new template was stored during resolution, persist it
	if templateStored {
		woc.updated = true
	}

	name := getStepOrDAGTaskName(nodeName)
	return woc.hasOutputResultRef(ctx, name, parentTemplate), nil
}

func (woc *wfOperationCtx) fetchWorkflowSpec(ctx context.Context) (wfv1.WorkflowSpecHolder, error) {
	if woc.wf.Spec.WorkflowTemplateRef == nil { //nolint:forbidigo // not-woc-misuse
		return nil, fmt.Errorf("cannot fetch workflow spec without workflowTemplateRef")
	}

	var specHolder wfv1.WorkflowSpecHolder
	var err error
	// Logic for workflow refers Workflow template
	if woc.wf.Spec.WorkflowTemplateRef.ClusterScope { //nolint:forbidigo // not-woc-misuse
		if woc.controller.cwftmplInformer == nil {
			woc.log.WithError(err).Error(ctx, "clusterWorkflowTemplate RBAC is missing")
			return nil, fmt.Errorf("cannot get resource clusterWorkflowTemplate at cluster scope")
		}
		woc.controller.metrics.CountWorkflowTemplate(ctx, metrics.WorkflowNew, woc.wf.Spec.WorkflowTemplateRef.Name, woc.wf.Namespace, true) //nolint:forbidigo // not-woc-misuse
		specHolder, err = woc.controller.cwftmplInformer.Lister().Get(woc.wf.Spec.WorkflowTemplateRef.Name)                                  //nolint:forbidigo // not-woc-misuse
	} else {
		woc.controller.metrics.CountWorkflowTemplate(ctx, metrics.WorkflowNew, woc.wf.Spec.WorkflowTemplateRef.Name, woc.wf.Namespace, false)  //nolint:forbidigo // not-woc-misuse
		specHolder, err = woc.controller.wftmplInformer.Lister().WorkflowTemplates(woc.wf.Namespace).Get(woc.wf.Spec.WorkflowTemplateRef.Name) //nolint:forbidigo // not-woc-misuse
	}
	if err != nil {
		return nil, err

View on GitHub (pinned to 35bff19146)

Solutions

  1. Convert the workflow to reference a WorkflowTemplate by setting spec.workflowTemplateRef.name
  2. Check the submitted YAML: workflowTemplateRef must be a direct child of spec, not nested elsewhere
  3. If inline specs are intended, ensure the controller code path taken does not require fetching the template spec (not in reference/secure mode)

Example fix

// before
spec:
  entrypoint: main
  templates:
    - name: main
      container:
        image: alpine
// after
spec:
  entrypoint: main
  workflowTemplateRef:
    name: my-template
Defensive patterns

Strategy: validation

Validate before calling

if wf.Spec.WorkflowTemplateRef == nil {
    return fmt.Errorf("workflow %s must set spec.workflowTemplateRef", wf.Name)
}

Type guard

func hasTemplateRef(wf *wfv1.Workflow) bool { return wf != nil && wf.Spec.WorkflowTemplateRef != nil }

Prevention

When it happens

Trigger: Reconciling a Workflow whose Spec.WorkflowTemplateRef is nil while executing the code path that requires fetching the referenced template spec (e.g. template referencing / stored-spec resolution in operator.go).

Common situations: Users submit workflows with fully inline specs (the traditional style) while the controller or code path assumes template-based workflows; older workflows migrated before workflowTemplateRef existed; YAML that accidentally places workflowTemplateRef at the wrong nesting level so it is not parsed.

Related errors


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