argoproj/argo-workflows · error
Error occurred during strategic merge patch
Error message
Error occurred during strategic merge patch
What it means
After converting the patch to JSON, ApplyPodSpecPatch applies it to the base PodSpec using sigs.k8s.io/structured-merge-diff's strategicpatch.StrategicMergePatch with apiv1.PodSpec as the schema. If the merge itself fails — typically because the patch JSON, while valid, is structurally incompatible with PodSpec's merge strategy (e.g. wrong types for patch-merge keys, non-object where an object is required) — the error is wrapped as "Error occurred during strategic merge patch".
Source
Thrown at workflow/util/util.go:1699
if err != nil {
return nil, errors.Wrap(err, "", "Failed to marshal the Pod spec")
}
for _, podSpecPatchYaml := range podSpecPatchYamls {
// must convert to json because PodSpec has only json tags
podSpecPatchJSON, convertErr := ConvertYAMLToJSON(podSpecPatchYaml)
if convertErr != nil {
return nil, errors.Wrap(convertErr, "", "Failed to convert the PodSpecPatch yaml to json")
}
// validate the patch to be a PodSpec
if unmarshalErr := json.Unmarshal([]byte(podSpecPatchJSON), &apiv1.PodSpec{}); unmarshalErr != nil {
return nil, fmt.Errorf("invalid podSpecPatch %q: %w", podSpecPatchYaml, unmarshalErr)
}
podSpecJSON, err = strategicpatch.StrategicMergePatch(podSpecJSON, []byte(podSpecPatchJSON), apiv1.PodSpec{})
if err != nil {
return nil, errors.Wrap(err, "", "Error occurred during strategic merge patch")
}
}
var newPodSpec apiv1.PodSpec
err = json.Unmarshal(podSpecJSON, &newPodSpec)
if err != nil {
return nil, errors.Wrap(err, "", "Error in Unmarshalling after merge the patch")
}
return &newPodSpec, nil
}
func GetNodeType(tmpl *wfv1.Template) wfv1.NodeType {
return tmpl.GetNodeType()
}
// IsWindowsUNCPath checks if path is prefixed with \\
// This can be used to skip any processing of paths
// that point to SMB shares, local named pipes and local UNC pathView on GitHub (pinned to 35bff19146)
Solutions
- Validate the patch against the PodSpec schema: run `kubectl patch --dry-run=server pod ... -p <patch>` or use kubectl's client-side validation to see the precise merge failure.
- Fix the patch shape: containers/volumes must be arrays; align types with the Kubernetes API (booleans as booleans, ints as ints).
- Note the code separately rejects patches that don't unmarshal into PodSpec with a clearer message — if you see this wrap instead, the JSON parsed but the merge strategy failed; simplify the patch and add fields back one at a time.
Example fix
# before
podSpecPatch: |
containers: # object instead of list -> merge error
name: main
image: my-image
# after
podSpecPatch: |
containers:
- name: main # list of objects, merged by name (patchMergeKey)
image: my-image Defensive patterns
Strategy: validation
Validate before calling
# shell: dry-run the same merge against Kubernetes to catch shape errors
kubectl patch pod <test-pod> --dry-run=server --type=strategic -p "$(cat podspec-patch.yaml)"
# Go: verify the patch unmarshals as a PodSpec before merging
var ps apiv1.PodSpec
b, _ := util.ConvertYAMLToJSON(patch)
if err := json.Unmarshal(b, &ps); err != nil { return fmt.Errorf("patch is not a PodSpec: %w", err) } Try / catch
// Go: detect merge failures and point at patch shape
if _, err := util.ApplyPodSpecPatch(podSpec, patchYaml); err != nil {
if strings.Contains(err.Error(), "Error occurred during strategic merge patch") {
return fmt.Errorf("podSpecPatch structurally incompatible with PodSpec (lists like containers must be arrays merged by name): %w", err)
}
return err
} Prevention
- Model list fields (containers, volumes, env) as arrays in patches, merged by their patchMergeKey (name).
- Use kubectl --dry-run=server to validate patch shape before wiring it into workflows.
- Add fields to patches incrementally to isolate which key breaks the merge.
- Keep types exact: booleans as booleans, integers as integers — no quoted values.
When it happens
Trigger: Calling ApplyPodSpecPatch where podSpecJSON and podSpecPatchJSON are both valid JSON but the strategic merge cannot be computed — e.g. patch sets containers to a string instead of a list, or uses a map where patchMergeKey semantics require a list of objects.
Common situations: Writing podSpecPatch with containers as an object instead of an array; using incorrect types for fields (e.g. string "true" for a boolean); JSON/YAML conversion producing a shape Kubernetes can't merge; subtle mismatch after passing patches through multiple templating layers.
Related errors
- Failed to convert the PodSpecPatch yaml to json
- Internal
- Failed to marshal the Pod spec
- Error in Unmarshalling after merge the patch
- invalid TTL
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/4be0dac5bdf20be1.
Report an issue: GitHub.