{"record":{"id":"dd7f7cd87ad686a1","repo":"wavetermdev/waveterm","slug":"input-must-be-a-struct-or-pointer-to-struct-got","errorCode":null,"errorMessage":"input must be a struct or pointer to struct, got %v","messagePattern":"input must be a struct or pointer to struct, got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/util/utilfn/marshal.go","lineNumber":102,"sourceCode":"\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)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc StructToMap(in any) (map[string]any, error) {\n\t// Get value and handle pointer\n\tval := reflect.ValueOf(in)\n\tif val.Kind() == reflect.Ptr {\n\t\tval = val.Elem()\n\t}\n\n\t// Check that we have a struct\n\tif val.Kind() != reflect.Struct {\n\t\treturn nil, fmt.Errorf(\"input must be a struct or pointer to struct, got %v\", val.Kind())\n\t}\n\n\t// Get type information\n\ttyp := val.Type()\n\tout := make(map[string]any)\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\tout[name] = val.Field(i).Interface()\n\t}","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/util/utilfn/marshal.go#L84-L120","documentation":"StructToMap converts a struct to map[string]any via reflection; after dereferencing one pointer level, the value must be a struct kind. This error reports the actual kind when the input is a map, slice, primitive, or a double pointer whose dereference is still not a struct.","triggerScenarios":"Calling StructToMap(someMap) (wrong direction — that's MapToStruct's job); passing a *[]T or **T; passing nil interface; passing a primitive like an int or string.","commonSituations":"Swapping the two helper functions by mistake in serialization glue; generic code where the type parameter isn't constrained to structs; passing results of another reflection-based call straight back in.","solutions":["Pass the struct itself or a single pointer to it: StructToMap(&cfg) or StructToMap(cfg)","If you have a map already, you don't need conversion — use it directly or call MapToStruct for the opposite direction","Constrain generics with [T any] + a runtime struct check, or [T struct] style patterns where possible","Dereference extra pointer layers before the call if your data comes from *T producers"],"exampleFix":"// before\nm, err := utilfn.StructToMap(rawMap) // input must be a struct or pointer to struct, got map\n// after\ntype Config struct{ Name string `json:\"name\"` }\nvar cfg Config\nm, err := utilfn.StructToMap(&cfg)","handlingStrategy":"type-guard","validationCode":"func isStructOrPtr(v any) bool {\n    rv := reflect.ValueOf(v)\n    if rv.Kind() == reflect.Ptr { rv = rv.Elem() }\n    return rv.Kind() == reflect.Struct\n}","typeGuard":"func isStructOrPtr(v any) bool {\n    rv := reflect.ValueOf(v)\n    if rv.Kind() == reflect.Ptr { rv = rv.Elem() }\n    return rv.Kind() == reflect.Struct\n}","tryCatchPattern":"m, err := utilfn.StructToMap(v)\nif err != nil && strings.Contains(err.Error(), \"must be a struct\") {\n    // wrong helper or wrong type: check %T of v\n}","preventionTips":["Use StructToMap only for structs; use MapToStruct for maps","Avoid passing **T or primitives into reflection helpers","Constrain generic helpers to struct types"],"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"}