{"record":{"id":"137fe4945b9b5ae5","repo":"wavetermdev/waveterm","slug":"out-parameter-must-be-a-pointer-to-struct-got-poi-137fe4","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":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tsunami/util/marshal.go","lineNumber":22,"sourceCode":"package util\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\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":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/tsunami/util/marshal.go#L4-L40","documentation":"After confirming out is a pointer, MapToStruct checks that the pointed-to value is a struct, because field-by-field reflection only works on struct kinds. A pointer to a map, slice, string, or other non-struct kind yields \"out parameter must be a pointer to struct, got pointer to %v\".","triggerScenarios":"MapToStruct(m, &someMap) with someMap of type map[string]any; MapToStruct(m, &someSlice); MapToStruct(m, &str).","commonSituations":"Trying to reuse MapToStruct as a generic map-to-value decoder for maps/slices; passing &out where out is already map[string]any from a JSON decode; mismatch between the component function's return type and the caller's decode target.","solutions":["Declare the out target as a struct (or pointer to struct) whose json tags match the map keys","If the target is a map, skip MapToStruct and index the map directly","For slices, write a dedicated loop or change the call site to decode into a struct"],"exampleFix":"// before\nvar out map[string]any\nutil.MapToStruct(m, &out) // pointer to map, not struct\n// after\ntype result struct {\n    Name string `json:\"name\"`\n    Size int    `json:\"size\"`\n}\nvar out result\nif err := util.MapToStruct(m, &out); err != nil { return err }","handlingStrategy":"validation","validationCode":"rv := reflect.ValueOf(out)\nif rv.Kind() == reflect.Ptr && rv.Elem().Kind() != reflect.Struct {\n    return fmt.Errorf(\"target must be *struct, got *%v\", rv.Elem().Kind())\n}","typeGuard":"func isStructPtr(v any) bool {\n    rv := reflect.ValueOf(v)\n    return rv.Kind() == reflect.Ptr && rv.Elem().Kind() == reflect.Struct\n}","tryCatchPattern":"if err := util.MapToStruct(m, &result); err != nil {\n    return fmt.Errorf(\"decode failed (need *struct target): %w\", err)\n}","preventionTips":["Decode into a struct with json tags matching the map keys, never into a map/slice","Index maps directly instead of routing them through MapToStruct","Add a startup test that decodes a sample map into every decode target struct"],"tags":["go","reflection","struct-required","marshaling"],"backgroundTag":"pointer-output-required","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}