argoproj/argo-workflows · error

Error in Unmarshalling after merge the patch

Error message

Error in Unmarshalling after merge the patch

What it means

This error is returned by the pod-spec merge helper in workflow/util when the JSON produced after applying a strategic-merge patch cannot be unmarshalled back into an apiv1.PodSpec. It means the merge-patch result is not a valid PodSpec JSON document, so Argo cannot construct the patched pod spec for the workflow step. It wraps the underlying json.Unmarshal error, which names the actual decoding problem and byte offset.

Source

Thrown at workflow/util/util.go:1706

		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 path
func IsWindowsUNCPath(path string, tmpl *wfv1.Template) bool {
	if !HasWindowsOSNodeSelector(tmpl.NodeSelector) && nruntime.GOOS != "windows" {
		return false
	}
	// Check for UNC prefix \\
	if strings.HasPrefix(path, `\\`) {
		return true

View on GitHub (pinned to 35bff19146)

Solutions

  1. Read the wrapped underlying error: it identifies the JSON path/type that failed to decode
  2. Print/log podSpecJSON and validate it with json.Unmarshal into a map or k8s PodSpec before calling
  3. Fix the merge patch so the resulting document is a valid PodSpec object
  4. Align k8s.io/api versions so PodSpec field types match between producer and consumer

Example fix

// before: patch produces e.g. `{"containers": "nginx"}` (string instead of array)
// after: correct patch shape
patch := []byte(`{"spec":{"containers":[{"name":"main","image":"nginx"}]}}`)
Defensive patterns

Strategy: try-catch

Validate before calling

var probe map[string]any
if err := json.Unmarshal(podSpecJSON, &probe); err != nil {
    return fmt.Errorf("podSpecJSON is not valid JSON: %w", err)
}

Type guard

func isObject(b []byte) bool {
    var m map[string]any
    return json.Unmarshal(b, &m) == nil && m != nil
}

Try / catch

spec, err := mergePatchPodSpec(base, patch)
if err != nil {
    var uerr *errors.ArgoError
    if stderrors.As(err, &uerr) {
        log.Errorf("pod spec unmarshal failed: %v", uerr)
    }
    return err
}

Prevention

When it happens

Trigger: Calling the merge-pod-spec helper (podSpec merge used when patching workflow pod specs) with a merge patch whose application yields malformed or non-PodSpec JSON; corrupted podSpecJSON input; a patch that replaces the spec with a wrong-typed structure (e.g. an array or string instead of an object).

Common situations: Custom mutating webhooks or external controllers producing malformed patched specs; version skew between k8s API libs causing field-type changes; manually crafted patches submitted via API/CI that serialize containers or volumes with wrong types.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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