{"record":{"id":"4ca4ff8fdf959dd7","repo":"GoogleContainerTools/skaffold","slug":"unmarshalling-new-v","errorCode":null,"errorMessage":"unmarshalling new: %v","messagePattern":"unmarshalling new: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/skaffold/util/util.go","lineNumber":235,"sourceCode":"\t\tif line := scanner.Text(); len(line) > 0 {\n\t\t\tresult = append(result, line)\n\t\t}\n\t}\n\treturn result\n}\n\n// CloneThroughJSON clones an `old` object into a `new` one\n// using json marshalling and unmarshalling.\n// Since the object can be marshalled, it's almost sure it can be\n// unmarshalled. So we prefer to panic instead of returning an error\n// that would create an untestable branch on the call site.\nfunc CloneThroughJSON(old interface{}, new interface{}) {\n\to, err := json.Marshal(old)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"marshalling old: %v\", err))\n\t}\n\tif err := json.Unmarshal(o, new); err != nil {\n\t\tpanic(fmt.Sprintf(\"unmarshalling new: %v\", err))\n\t}\n}\n\n// CloneThroughYAML clones an `old` object into a `new` one\n// using yaml marshalling and unmarshalling.\n// Since the object can be marshalled, it's almost sure it can be\n// unmarshalled. So we prefer to panic instead of returning an error\n// that would create an untestable branch on the call site.\nfunc CloneThroughYAML(old interface{}, new interface{}) {\n\tcontents, err := yaml.Marshal(old)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"marshalling old: %v\", err))\n\t}\n\tif err := yaml.Unmarshal(contents, new); err != nil {\n\t\tpanic(fmt.Sprintf(\"unmarshalling new: %v\", err))\n\t}\n}\n","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/GoogleContainerTools/skaffold/blob/a1189de023efc32d4b8e11f395acc678aa555011/pkg/skaffold/util/util.go#L217-L253","documentation":"CloneThroughJSON's second panic fires when the JSON produced from `old` cannot be unmarshalled into `new`. The library assumes this is near-impossible (same data round-trips), so it panics instead of returning an error, keeping call sites simple.","triggerScenarios":"Calling CloneThroughJSON(old, new) where `new` has a type incompatible with `old`'s JSON shape — e.g. cloning into a struct with different field types, a non-pointer destination, or mismatched numeric/string fields.","commonSituations":"Refactoring one of the two types so field types diverge (string vs int, object vs slice); passing a value instead of a pointer for `new`; cloning between unrelated structs.","solutions":["Ensure `new` is a pointer to a type structurally compatible with `old`","Re-align field names/types after a refactor so the JSON round-trip is lossless","Add a regression test that clones the struct and compares the result to the original"],"exampleFix":"// before\nvar copy Config\nCloneThroughJSON(cfg, copy) // copy not a pointer; unmarshal fails\n// after\nvar copy Config\nCloneThroughJSON(cfg, &copy)","handlingStrategy":"type-guard","validationCode":"func cloneCompatible(old, new interface{}) error {\n  ob, err := json.Marshal(old)\n  if err != nil { return err }\n  return json.Unmarshal(ob, new)\n}","typeGuard":"func isPointerToStruct(v interface{}) bool {\n  rv := reflect.ValueOf(v)\n  return rv.Kind() == reflect.Ptr && rv.Elem().Kind() == reflect.Struct\n}","tryCatchPattern":"func cloneSafe(old, new interface{}) (err error) {\n  defer func() { if r := recover(); r != nil { err = fmt.Errorf(\"clone failed: %v\", r) } }()\n  util.CloneThroughJSON(old, new)\n  return nil\n}","preventionTips":["Always pass a pointer for the destination argument","Keep source and destination struct field types aligned after refactors","Assert clone equality in unit tests (reflect.DeepEqual after clone)"],"tags":["json","serialization","panic","clone"],"backgroundTag":"json-unmarshal-type-mismatch","analyzedSha":"a1189de023efc32d4b8e11f395acc678aa555011","analyzedAt":"2026-09-05T12:09:27.064Z","contentChangedAt":"2026-09-05T12:09:27.064Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}