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.ConfigMapView on GitHub (pinned to 35bff19146)
Solutions
- Fix the variable names in podSpecPatch to reference defined parameters
- Ensure referenced parameters exist (or add them) as workflow/template parameters
- 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
- Reference only defined global/workflow/template parameters in podSpecPatch
- Test podSpecPatch with a minimal workflow first
- Avoid hand-editing variable names in YAML without linting
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
- Error applying PodSpecPatch
- failed to read container args file %s: %w
- failed to unmarshal container args: %w
- failed to read template: %w
- failed to start command: %w
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/3d3266206639da2c.
Report an issue: GitHub.