{"record":{"id":"4be0dac5bdf20be1","repo":"argoproj/argo-workflows","slug":"error-occurred-during-strategic-merge-patch","errorCode":null,"errorMessage":"Error occurred during strategic merge patch","messagePattern":"Error occurred during strategic merge patch","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/util/util.go","lineNumber":1699,"sourceCode":"\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"\", \"Failed to marshal the Pod spec\")\n\t}\n\n\tfor _, podSpecPatchYaml := range podSpecPatchYamls {\n\t\t// must convert to json because PodSpec has only json tags\n\t\tpodSpecPatchJSON, convertErr := ConvertYAMLToJSON(podSpecPatchYaml)\n\t\tif convertErr != nil {\n\t\t\treturn nil, errors.Wrap(convertErr, \"\", \"Failed to convert the PodSpecPatch yaml to json\")\n\t\t}\n\n\t\t// validate the patch to be a PodSpec\n\t\tif unmarshalErr := json.Unmarshal([]byte(podSpecPatchJSON), &apiv1.PodSpec{}); unmarshalErr != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid podSpecPatch %q: %w\", podSpecPatchYaml, unmarshalErr)\n\t\t}\n\n\t\tpodSpecJSON, err = strategicpatch.StrategicMergePatch(podSpecJSON, []byte(podSpecPatchJSON), apiv1.PodSpec{})\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrap(err, \"\", \"Error occurred during strategic merge patch\")\n\t\t}\n\t}\n\n\tvar newPodSpec apiv1.PodSpec\n\terr = json.Unmarshal(podSpecJSON, &newPodSpec)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"\", \"Error in Unmarshalling after merge the patch\")\n\t}\n\treturn &newPodSpec, nil\n}\n\nfunc GetNodeType(tmpl *wfv1.Template) wfv1.NodeType {\n\treturn tmpl.GetNodeType()\n}\n\n// IsWindowsUNCPath checks if path is prefixed with \\\\\n// This can be used to skip any processing of paths\n// that point to SMB shares, local named pipes and local UNC path","sourceCodeStart":1681,"sourceCodeEnd":1717,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/util/util.go#L1681-L1717","documentation":"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\".","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\npodSpecPatch: |\n  containers:            # object instead of list -> merge error\n    name: main\n    image: my-image\n# after\npodSpecPatch: |\n  containers:\n    - name: main         # list of objects, merged by name (patchMergeKey)\n      image: my-image","handlingStrategy":"validation","validationCode":"# shell: dry-run the same merge against Kubernetes to catch shape errors\nkubectl patch pod <test-pod> --dry-run=server --type=strategic -p \"$(cat podspec-patch.yaml)\"\n# Go: verify the patch unmarshals as a PodSpec before merging\nvar ps apiv1.PodSpec\nb, _ := util.ConvertYAMLToJSON(patch)\nif err := json.Unmarshal(b, &ps); err != nil { return fmt.Errorf(\"patch is not a PodSpec: %w\", err) }","typeGuard":null,"tryCatchPattern":"// Go: detect merge failures and point at patch shape\nif _, err := util.ApplyPodSpecPatch(podSpec, patchYaml); err != nil {\n\tif strings.Contains(err.Error(), \"Error occurred during strategic merge patch\") {\n\t\treturn fmt.Errorf(\"podSpecPatch structurally incompatible with PodSpec (lists like containers must be arrays merged by name): %w\", err)\n\t}\n\treturn err\n}","preventionTips":["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."],"tags":["podspec","strategic-merge-patch","json","patch"],"backgroundTag":"strategic-merge-patch-failed","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}