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

  1. Fix the type's UnmarshalJSON to accept its own MarshalJSON output
  2. Remove custom marshaler asymmetry or unsupported field types from the struct being merged
  3. 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

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


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)