GoogleContainerTools/skaffold · error

marshalling yaml: %w

Error message

marshalling yaml: %w

What it means

ManifestList.Visit unmarshals each Kubernetes manifest into a generic map, applies a FieldVisitor transformation, and re-marshals the map back to YAML. This error wraps any failure from yaml.Marshal on the transformed map. Since the input came from valid YAML, this usually means a visitor injected a value that cannot be serialized back to YAML (e.g. a channel, func, or recursive structure), or a type marshalling incompatibility.

Source

Thrown at pkg/skaffold/kubernetes/manifest/visitor.go:233

// Visit recursively visits all transformable object fields within the manifests and lets the visitor apply transformations/aggregations on them.
func (l *ManifestList) Visit(visitor FieldVisitor, rs ResourceSelector) (ManifestList, error) {
	var updated ManifestList

	for _, manifest := range *l {
		m := make(map[string]interface{})
		if err := yaml.Unmarshal(manifest, &m); err != nil {
			return nil, fmt.Errorf("reading Kubernetes YAML: %w", err)
		}

		if len(m) == 0 {
			continue
		}

		traverseManifestFields(m, visitor, rs)

		updatedManifest, err := yaml.Marshal(m)
		if err != nil {
			return nil, fmt.Errorf("marshalling yaml: %w", err)
		}

		updated = append(updated, updatedManifest)
	}

	return updated, nil
}

// traverseManifest traverses all transformable fields contained within the manifest.
func traverseManifestFields(manifest map[string]interface{}, visitor FieldVisitor, rs ResourceSelector) {
	var groupKind apimachinery.GroupKind
	var apiVersion string
	if value, ok := manifest["apiVersion"].(string); ok {
		apiVersion = value
	}
	var kind string
	if value, ok := manifest["kind"].(string); ok {
		kind = value

View on GitHub (pinned to a1189de023)

Solutions

  1. Inspect the wrapped error (%w cause) to identify which value failed to marshal
  2. Fix the FieldVisitor so it only writes YAML-serializable values (string, number, bool, map, slice) into the manifest
  3. Ensure consistent yaml library versions; convert yaml.v2-specific types (MapSlice, JSONMapSlice) to plain map[string]interface{} before mutation
  4. If caused by a concurrency bug, ensure the manifest map is not modified from multiple goroutines while Visit runs

Example fix

// before
obj["field"] = make(chan int)
// after
obj["field"] = "serializable-string-value"
Defensive patterns

Strategy: validation

Validate before calling

// ensure all values in the manifest map are YAML-serializable before Visit
func serializable(v interface{}) bool {
	switch v.(type) {
	case string, int, int64, float64, bool, nil:
		return true
	case map[string]interface{}:
		for _, vv := range v.(map[string]interface{}) {
			if !serializable(vv) { return false }
		}
		return true
	case []interface{}:
		for _, vv := range v.([]interface{}) {
			if !serializable(vv) { return false }
		}
		return true
	default:
		return false
	}
}

Try / catch

updated, err := manifests.Visit(visitor, rs)
if err != nil {
	return fmt.Errorf("manifest transform failed: %w", err) // inspect wrapped yaml cause
}

Prevention

When it happens

Trigger: Calling any of SetPlatformNodeAffinity, GetImagePlatforms, SetGKEARMToleration, GetImages, replaceImages, or SetLabels on a ManifestList when a FieldVisitor has mutated the manifest map with a value the YAML encoder cannot handle, or when a custom ResourceSelector injects unserializable values.

Common situations: Custom visitor plugins that put non-serializable values (channels, funcs, cyclic pointers) into the manifest map; YAML marshalling libraries with incompatible type registration (yaml.v2 vs yaml.v3 types like MapSlice); maps mutated concurrently during traversal.

Related errors


AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05). Data as JSON: /api/errors/88ae15e57386e05a. Report an issue: GitHub.