{"record":{"id":"b0b50015f23bd9c8","repo":"microsoft/aspire","slug":"aspire-merge-type-mismatch-cannot-merge-v-into-v","errorCode":null,"errorMessage":"aspire: merge type mismatch: cannot merge %v into %v","messagePattern":"aspire: merge type mismatch: cannot merge (.+?) into (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.CodeGeneration.Go/Resources/base.go","lineNumber":848,"sourceCode":"\t}\n\n\treturn structValue.Interface().(T), true\n}\n\n// ── deepUpdate ───────────────────────────────────────────────────────────────\n//\n// Used by generated code to merge variadic Options structs:\n//\n//\tmerged := &AddRedisOptions{}\n//\tfor _, opt := range options {\n//\t    if opt != nil { merged = deepUpdate(merged, opt) }\n//\t}\nfunc deepUpdate[T any](dst, src T) T {\n\tdstType := reflect.TypeOf(dst)\n\tsrcType := reflect.TypeOf(src)\n\n\tif dstType != srcType {\n\t\tpanic(fmt.Sprintf(\"aspire: merge type mismatch: cannot merge %v into %v\", srcType, dstType))\n\t}\n\n\tvar nilT T\n\tif reflect.DeepEqual(src, nilT) {\n\t\treturn dst\n\t}\n\tif reflect.DeepEqual(dst, nilT) {\n\t\treturn src\n\t}\n\n\tv := reflect.ValueOf(dst)\n\tkind := v.Kind()\n\tif kind == reflect.Ptr {\n\t\tkind = v.Elem().Kind()\n\t}\n\n\tswitch kind {\n\tcase reflect.Map, reflect.Struct:","sourceCodeStart":830,"sourceCodeEnd":866,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.CodeGeneration.Go/Resources/base.go#L830-L866","documentation":"deepUpdate is a generic merge helper used by generated mergeable methods (WithMergeLogging, AddTestRedis, etc.). It panics when the source and destination values have different reflect.TypeOf because merging mismatched types would be meaningless. Since it panics rather than returns an error, callers must ensure type compatibility before invoking merge-style APIs.","triggerScenarios":"Calling a merge-style generated method (e.g. WithMergeLogging/WithOptionalString/AddTestRedis) with a value whose concrete type differs from the existing/target value's type — e.g. merging *Config into Config, or a different struct type into an existing one.","commonSituations":"Passing a value type where a pointer is expected (or vice versa), passing a different options struct than the one originally set, or misuse of the generated deepUpdate in custom code.","solutions":["Pass a value whose concrete type exactly matches the target (same struct type, same pointer-ness)","Dereference or take the address of the argument to match the expected form before merging","Ensure you are calling the intended merge overload for the type you have"],"exampleFix":"// before\nopts := Options{A: 1}\ndeepUpdate(existingPtr, opts) // mismatch: *Options vs Options\n// after\ndeepUpdate(existingPtr, &opts)","handlingStrategy":"type-guard","validationCode":"if reflect.TypeOf(src) != reflect.TypeOf(dst) {\n\treturn errors.New(\"cannot merge: source and destination types differ\")\n}","typeGuard":"func sameConcreteType(a, b any) bool { return reflect.TypeOf(a) == reflect.TypeOf(b) }","tryCatchPattern":"defer func() {\n\tif r := recover(); r != nil {\n\t\treturn fmt.Errorf(\"merge failed: %v\", r)\n\t}\n}()","preventionTips":["Match pointer-ness exactly when passing merge arguments","Merge the same options struct type that was originally set","Add a unit test exercising merge-style methods with your option types"],"tags":["go","reflection","merge","panic","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}