microsoft/aspire · error
aspire: merge error
Error message
aspire: merge error: %v
What it means
After a type check, deepUpdate marshals the merged value to JSON and unmarshals it into a fresh T. If json.Unmarshal fails (the JSON produced by marshal cannot be decoded into T — practically impossible for well-formed structs, but possible with custom marshalers or unsupported field shapes), the helper panics wrapping the unmarshal error.
Solutions
- Fix the type's UnmarshalJSON to accept its own MarshalJSON output
- Remove custom marshaler asymmetry or unsupported field types from the struct being merged
- Update the merge argument to a plain, JSON-round-trippable struct
Example fix
// before
func (t T) MarshalJSON() ([]byte, error) { return []byte(`"str"`), nil }
// UnmarshalJSON expects an object -> unmarshal fails
// after
func (t T) UnmarshalJSON(b []byte) error { /* accept the string form too */ ... } Defensive patterns
Strategy: type-guard
Validate before calling
// Verify the value survives a JSON round trip
b, err := json.Marshal(v)
if err == nil {
var probe T
if err := json.Unmarshal(b, &probe); err != nil { /* incompatible marshal/unmarshal */ }
} Type guard
func jsonRoundTrips[T any](v T) bool {
b, err := json.Marshal(v)
if err != nil { return false }
var out T
return json.Unmarshal(b, &out) == nil
} Try / catch
defer func() {
if r := recover(); r != nil {
return fmt.Errorf("merge failed: %v", r)
}
}() Prevention
- Ensure custom MarshalJSON/UnmarshalJSON are symmetric
- Avoid fields with channel/func types or NaN floats in merged structs
- Unit-test merge methods with every option struct you use
When it happens
Trigger: Calling a merge-style generated method where the value's MarshalJSON output cannot be unmarshaled into T — e.g. a type with a custom MarshalJSON emitting a shape incompatible with its own struct, or a field type UnmarshalJSON rejects.
Common situations: Custom JSON marshaler/unmarshaler asymmetry in user types merged via WithOptionalCallback/WithOptionalString-style APIs, embedded unsupported types (channels, funcs leaking through custom marshalers), or NaN in fields serialized through a custom path.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- aspire: merge type mismatch: cannot merge
- -32000
- A JSON Patch path can only traverse objects and arrays.
- A JSON Patch path cannot traverse a null value.
- argument ' ' passed to capability ' ' contains a circular…
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/ed7ed8bcbedffb9e.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.CodeGeneration.Go/Resources/base.go:879
if kind == reflect.Ptr {
kind = v.Elem().Kind()
}
switch kind {
case reflect.Map, reflect.Struct:
dstMap, _ := toMap(dst)
srcMap, _ := toMap(src)
mergedMap := merge(dstMap, srcMap, 0)
bytes, _ := json.Marshal(mergedMap)
var result T
if reflect.TypeOf(result).Kind() == reflect.Ptr {
result = reflect.New(reflect.TypeOf(result).Elem()).Interface().(T)
}
if err := json.Unmarshal(bytes, &result); err != nil {
panic(fmt.Sprintf("aspire: merge error: %v", err))
}
return result
default:
return src
}
}
func merge(dst, src map[string]any, depth int) map[string]any {
const depthLimit = 32
if depth > depthLimit {
panic("aspire: deep update recursion limit of '32' exceeded")
}
for key, srcVal := range src {
if dstVal, ok := dst[key]; ok {
srcSub, srcOk := toMap(srcVal)
dstSub, dstOk := toMap(dstVal)
View on GitHub (pinned to 25830f84bd)