{"record":{"id":"031e85cac12fae03","repo":"wavetermdev/waveterm","slug":"out-parameter-must-be-a-pointer-to-struct-got-poi","errorCode":null,"errorMessage":"out parameter must be a pointer to struct, got pointer to %v","messagePattern":"out parameter must be a pointer to struct, got pointer to (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/util/utilfn/marshal.go","lineNumber":67,"sourceCode":"\t}\n\tdecoder, err := mapstructure.NewDecoder(dconfig)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn decoder.Decode(input)\n}\n\nfunc MapToStruct(in map[string]any, out any) error {\n\t// Check that out is a pointer\n\toutValue := reflect.ValueOf(out)\n\tif outValue.Kind() != reflect.Ptr {\n\t\treturn fmt.Errorf(\"out parameter must be a pointer, got %v\", outValue.Kind())\n\t}\n\n\t// Get the struct it points to\n\telem := outValue.Elem()\n\tif elem.Kind() != reflect.Struct {\n\t\treturn fmt.Errorf(\"out parameter must be a pointer to struct, got pointer to %v\", elem.Kind())\n\t}\n\n\t// Get type information\n\ttyp := elem.Type()\n\n\t// For each field in the struct\n\tfor i := 0; i < typ.NumField(); i++ {\n\t\tfield := typ.Field(i)\n\n\t\t// Skip unexported fields\n\t\tif !field.IsExported() {\n\t\t\tcontinue\n\t\t}\n\n\t\tname := getJSONName(field)\n\t\tif value, ok := in[name]; ok {\n\t\t\tif err := setValue(elem.Field(i), value); err != nil {\n\t\t\t\treturn fmt.Errorf(\"error setting field %s: %w\", name, err)","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/util/utilfn/marshal.go#L49-L85","documentation":"A second-stage guard in MapToStruct: out is a pointer, but reflect.Value.Elem() reveals it does not point to a struct (e.g. pointer to map or slice). The reflection field-walk only works on struct kinds, so the call is rejected with the pointed-to kind named.","triggerScenarios":"Calling MapToStruct(m, &someMap) or MapToStruct(m, &slice); passing **Config (pointer to pointer) where Elem() is a pointer, not a struct; a typed nil *Config works (Elem is struct) but a *map does not.","commonSituations":"Generic decode helpers receiving *map[string]any from callers who confused input and output types; refactors changing the destination type without updating MapToStruct calls; double-pointer wrapping from APIs that return *T.","solutions":["Pass a pointer to a struct: MapToStruct(m, &cfg), not &m","If the target is a **T, dereference first: MapToStruct(m, *cfgPtr)","If you actually want a map out, use StructToMap instead — the functions are directional","Add a Go generic constraint (out *T, T struct) so the compiler rejects wrong shapes"],"exampleFix":"// before\nout := map[string]any{}\nerr := utilfn.MapToStruct(in, &out) // pointer to map, not struct\n// after\ntype Config struct{ Name string `json:\"name\"` }\nvar cfg Config\nerr := utilfn.MapToStruct(in, &cfg)","handlingStrategy":"type-guard","validationCode":"if !isPtrToStruct(out) {\n    return errors.New(\"MapToStruct requires *struct\")\n}","typeGuard":"func isPtrToStruct(out any) bool {\n    v := reflect.ValueOf(out)\n    return v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct\n}","tryCatchPattern":"if err := utilfn.MapToStruct(m, out); err != nil {\n    if strings.Contains(err.Error(), \"pointer to struct\") {\n        // destination shape is wrong; inspect %T of out\n    }\n}","preventionTips":["Keep destinations as plain struct + &var, avoid **T","Use StructToMap when the target is a map, not MapToStruct","Add compile-time constraints or constructor helpers that enforce *struct"],"tags":["reflection","go","marshaling","types"],"backgroundTag":"pointer-required-argument","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}