argoproj/argo-workflows · error

Failed to substitute the PodSpecPatch variables

Error message

Failed to substitute the PodSpecPatch variables

What it means

Before building a pod, the controller substitutes global and local parameters into the template's podSpecPatch via common.ProcessArgs. If that substitution fails (bad variable reference, malformed template, missing parameter), the error is wrapped with this message so the user knows the patch stage — not the spec itself — was the problem.

Source

Thrown at workflow/controller/workflowpod.go:318

	podSpecPatches := []string{}
	localParams := make(map[string]string)
	if tmpl.IsPodType() {
		localParams[varkeys.PodName.Template()] = pod.Name
	}
	toProcess := []string{}
	if pb.in.execWfSpec.HasPodSpecPatch() {
		toProcess = append(toProcess, pb.in.execWfSpec.PodSpecPatch)
	}
	if tmpl.HasPodSpecPatch() {
		toProcess = append(toProcess, tmpl.PodSpecPatch)
	}

	for _, patch := range toProcess {
		newTmpl := tmpl.DeepCopy()
		newTmpl.PodSpecPatch = patch
		processedTmpl, err := common.ProcessArgs(ctx, newTmpl, &wfv1.Arguments{}, pb.in.globalParams, localParams, false, pb.in.namespace, pb.in.configMapIndexer)
		if err != nil {
			return nil, errors.Wrap(err, "", "Failed to substitute the PodSpecPatch variables")
		}
		podSpecPatches = append(podSpecPatches, processedTmpl.PodSpecPatch)
	}
	return podSpecPatches, nil
}

// podBuildResult is the output of the pure pod builder. It carries the
// constructed pod plus everything submitPod needs to apply as side effects.
// build performs ZERO k8s creates and ZERO status mutations: it only fills in
// this struct. submitPod consumes it and performs the impure operations in a
// correctness-critical order (ConfigMaps before the pod that mounts them).
type podBuildResult struct {
	// Pod is the fully constructed pod spec, ready to be created.
	Pod *apiv1.Pod
	// ExtraObjects are auxiliary objects that must be created before the pod.
	// Currently this is the env/args offload ConfigMap (mounted by the pod), so
	// it MUST be created first and AlreadyExists is tolerated.
	ExtraObjects []*apiv1.ConfigMap

View on GitHub (pinned to 35bff19146)

Solutions

  1. Fix the variable names in podSpecPatch to reference defined parameters
  2. Ensure referenced parameters exist (or add them) as workflow/template parameters
  3. Test the patch expression inline in a small workflow before deploying

Example fix

// before
podSpecPatch: '{"containers":[{"name":"main","image":"{{workflow.parameters.image}}"}]}' // image param not defined
// after
# add to workflow spec:
arguments:
  parameters:
    - name: image
      value: myimage:latest
Defensive patterns

Strategy: validation

Validate before calling

// check all {{vars}} in podSpecPatch exist in params
defined := map[string]bool{ "workflow.name": true, /* globals + template params */ }
for _, v := range regexp.MustCompile(`\{\{([^}]+)\}\}`).FindAllStringSubmatch(patch, -1) {
  if !defined[v[1]] { return fmt.Errorf("undefined var %s in podSpecPatch", v[1]) }
}

Try / catch

processedTmpl, err := common.ProcessArgs(ctx, tmpl, args, globals, locals, false, ns, indexer)
if err != nil {
  return nil, fmt.Errorf("podSpecPatch substitution failed: %w", err)
}

Prevention

When it happens

Trigger: A podSpecPatch containing {{...}} variables that are undefined in the scope (neither global params nor the local parameters passed), or ProcessArgs failing on an invalid argument spec.

Common situations: PodSpecPatch referencing parameters not defined in the template or workflow; patch written for a different template context; typos in variable names like {{workflow.namee}}.

Related errors


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