GoogleContainerTools/skaffold · error
unmarshalling new: %v
Error message
unmarshalling new: %v
What it means
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.
Source
Thrown at pkg/skaffold/util/util.go:235
if line := scanner.Text(); len(line) > 0 {
result = append(result, line)
}
}
return result
}
// CloneThroughJSON clones an `old` object into a `new` one
// using json marshalling and unmarshalling.
// Since the object can be marshalled, it's almost sure it can be
// unmarshalled. So we prefer to panic instead of returning an error
// that would create an untestable branch on the call site.
func CloneThroughJSON(old interface{}, new interface{}) {
o, err := json.Marshal(old)
if err != nil {
panic(fmt.Sprintf("marshalling old: %v", err))
}
if err := json.Unmarshal(o, new); err != nil {
panic(fmt.Sprintf("unmarshalling new: %v", err))
}
}
// CloneThroughYAML clones an `old` object into a `new` one
// using yaml marshalling and unmarshalling.
// Since the object can be marshalled, it's almost sure it can be
// unmarshalled. So we prefer to panic instead of returning an error
// that would create an untestable branch on the call site.
func CloneThroughYAML(old interface{}, new interface{}) {
contents, err := yaml.Marshal(old)
if err != nil {
panic(fmt.Sprintf("marshalling old: %v", err))
}
if err := yaml.Unmarshal(contents, new); err != nil {
panic(fmt.Sprintf("unmarshalling new: %v", err))
}
}
View on GitHub (pinned to a1189de023)
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
Example fix
// before var copy Config CloneThroughJSON(cfg, copy) // copy not a pointer; unmarshal fails // after var copy Config CloneThroughJSON(cfg, ©)
Defensive patterns
Strategy: type-guard
Validate before calling
func cloneCompatible(old, new interface{}) error {
ob, err := json.Marshal(old)
if err != nil { return err }
return json.Unmarshal(ob, new)
} Type guard
func isPointerToStruct(v interface{}) bool {
rv := reflect.ValueOf(v)
return rv.Kind() == reflect.Ptr && rv.Elem().Kind() == reflect.Struct
} Try / catch
func cloneSafe(old, new interface{}) (err error) {
defer func() { if r := recover(); r != nil { err = fmt.Errorf("clone failed: %v", r) } }()
util.CloneThroughJSON(old, new)
return nil
} Prevention
- 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)
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- marshalling old: %v
- marshalling event: %w
- unknown panic
- marshalling configuration: %w
- marshaling new config: %w
AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05).
Data as JSON: /api/errors/4ca4ff8fdf959dd7.
Report an issue: GitHub.