{"record":{"id":"f3312450be388d5e","repo":"kubernetes/kops","slug":"building-json-w","errorCode":null,"errorMessage":"building json: %w","messagePattern":"building json: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nodeup/pkg/model/containerd.go","lineNumber":460,"sourceCode":"        }\n    ]\n}\n`\n\n\t// We will gradually build up the schema here, as needed\n\ttype Route struct {\n\t\tDest string `json:\"dst\"`\n\t}\n\n\troutes := []Route{\n\t\t{Dest: \"0.0.0.0/0\"},\n\t}\n\tif b.IsIPv6Only() {\n\t\troutes = append(routes, Route{Dest: \"::/0\"})\n\t}\n\troutesJSON, err := json.Marshal(routes)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"building json: %w\", err)\n\t}\n\tcontents = strings.ReplaceAll(contents, \"{{Routes}}\", string(routesJSON))\n\n\tklog.V(8).Infof(\"Built containerd CNI config template\\n%s\", contents)\n\n\tc.AddTask(&nodetasks.File{\n\t\tPath:     \"/etc/containerd/config-cni.template\",\n\t\tContents: fi.NewStringResource(contents),\n\t\tType:     nodetasks.FileType_File,\n\t})\n\treturn nil\n}\n\n// buildContainerdConfig builds the containerd config used for containerd >= 2.0.\n// containerd 2.0 split io.containerd.grpc.v1.cri into io.containerd.cri.v1.runtime and io.containerd.cri.v1.images.\n// See https://github.com/containerd/containerd/blob/main/docs/cri/config.md\n// Apart from ConfigAdditions, the emitted fields load identically under config versions 3\n// and 4; see containerdConfigVersion.","sourceCodeStart":442,"sourceCodeEnd":478,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/nodeup/pkg/model/containerd.go#L442-L478","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the wrapped error (%w) in the message to identify which value failed to marshal","Check recent changes to the Route struct or routes slice in buildCNIConfigTemplateFile for non-JSON-serializable field types","Rebuild nodeup from an unmodified upstream version to confirm the failure is not from local patches","As a workaround, verify cluster networking config (UsesKubenet, IPv6-only setting) is correct, since this code path only runs for Kubenet"],"exampleFix":"// before (hypothetical breaking change)\ntype Route struct {\n    Dest string `json:\"dst\"`\n    Hook func() // not JSON-marshalable\n}\n// after\ntype Route struct {\n    Dest string `json:\"dst\"`\n}","handlingStrategy":"validation","validationCode":"// Marshal is deterministic for the fixed []Route input; guard struct changes in CI:\nroutes := []struct{ Dest string `json:\"dst\"` }{{Dest: \"0.0.0.0/0\"}}\nif _, err := json.Marshal(routes); err != nil {\n    return fmt.Errorf(\"building json: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if _, err := json.Marshal(routes); err != nil {\n    return fmt.Errorf(\"building json: %w\", err) // inspect %w for the failing value\n}","preventionTips":["Keep the Route struct limited to JSON-serializable primitive fields","Add a unit test that marshals the routes for both IPv4 and IPv6-only clusters","Run go vet / lint on changes to nodeup/pkg/model/containerd.go"],"tags":["containerd","json","kubenet","nodeup"],"backgroundTag":"json-marshal-failed","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}