kubernetes/kops · error

building json: %w

Error message

building json: %w

What it means

During generation of the containerd CNI config template for Kubenet networking (nodeup/pkg/model/containerd.go:460), the builder marshals a small slice of CNI Route structs to JSON to substitute into the {{Routes}} placeholder. This error wraps json.Marshal failure. In practice this is nearly impossible: the only data is two hardcoded string routes (0.0.0.0/0 and optionally ::/0 for IPv6-only clusters), and neither the Route struct nor its values can fail marshaling.

Source

Thrown at nodeup/pkg/model/containerd.go:460

        }
    ]
}
`

	// We will gradually build up the schema here, as needed
	type Route struct {
		Dest string `json:"dst"`
	}

	routes := []Route{
		{Dest: "0.0.0.0/0"},
	}
	if b.IsIPv6Only() {
		routes = append(routes, Route{Dest: "::/0"})
	}
	routesJSON, err := json.Marshal(routes)
	if err != nil {
		return fmt.Errorf("building json: %w", err)
	}
	contents = strings.ReplaceAll(contents, "{{Routes}}", string(routesJSON))

	klog.V(8).Infof("Built containerd CNI config template\n%s", contents)

	c.AddTask(&nodetasks.File{
		Path:     "/etc/containerd/config-cni.template",
		Contents: fi.NewStringResource(contents),
		Type:     nodetasks.FileType_File,
	})
	return nil
}

// buildContainerdConfig builds the containerd config used for containerd >= 2.0.
// containerd 2.0 split io.containerd.grpc.v1.cri into io.containerd.cri.v1.runtime and io.containerd.cri.v1.images.
// See https://github.com/containerd/containerd/blob/main/docs/cri/config.md
// Apart from ConfigAdditions, the emitted fields load identically under config versions 3
// and 4; see containerdConfigVersion.

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Inspect the wrapped error (%w) in the message to identify which value failed to marshal
  2. Check recent changes to the Route struct or routes slice in buildCNIConfigTemplateFile for non-JSON-serializable field types
  3. Rebuild nodeup from an unmodified upstream version to confirm the failure is not from local patches
  4. As a workaround, verify cluster networking config (UsesKubenet, IPv6-only setting) is correct, since this code path only runs for Kubenet

Example fix

// before (hypothetical breaking change)
type Route struct {
    Dest string `json:"dst"`
    Hook func() // not JSON-marshalable
}
// after
type Route struct {
    Dest string `json:"dst"`
}
Defensive patterns

Strategy: validation

Validate before calling

// Marshal is deterministic for the fixed []Route input; guard struct changes in CI:
routes := []struct{ Dest string `json:"dst"` }{{Dest: "0.0.0.0/0"}}
if _, err := json.Marshal(routes); err != nil {
    return fmt.Errorf("building json: %w", err)
}

Try / catch

if _, err := json.Marshal(routes); err != nil {
    return fmt.Errorf("building json: %w", err) // inspect %w for the failing value
}

Prevention

When it happens

Trigger: buildCNIConfigTemplateFile is invoked (cluster uses Kubenet, NodeupConfig.UsesKubenet) and json.Marshal(routes) returns an error. With the current fixed []Route{Dest string} input this cannot realistically occur; only a change to the struct (e.g. an unsupported field type like chan or func) would trigger it.

Common situations: A developer modifies the Route struct or routes slice in containerd.go to include a non-marshalable type (chan, func, complex, circular reference), then builds a Kubenet cluster; the nodeup build task fails at config template generation.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/f3312450be388d5e. Report an issue: GitHub.