{"record":{"id":"f0d527489f610658","repo":"wavetermdev/waveterm","slug":"input-must-be-a-struct-or-pointer-to-struct-got-f0d527","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":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tsunami/util/marshal.go","lineNumber":57,"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":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/tsunami/util/marshal.go#L39-L75","documentation":"StructToMap serializes a struct (or pointer to struct) into a map[string]any using json tags. It rejects any input whose underlying reflect.Kind is not struct after one level of pointer dereference, because there are no fields to iterate.","triggerScenarios":"Calling util.StructToMap(x) where x is a map, slice, string, int, nil interface, a **T (double pointer), or a nil *T (Elem() of nil pointer yields an invalid value, not a struct).","commonSituations":"Passing a nil pointer after a failed lookup; passing already-converted map output back into StructToMap by mistake; generic helper code where the type parameter ends up being a non-struct.","solutions":["Ensure you pass a struct or a single-level pointer to a struct: util.StructToMap(&myStruct).","Check for nil before calling: if ptr == nil { ... } — nil pointers cannot be converted.","If you have a **T or interface{}, unwrap to the struct first with reflection or a type assertion."],"exampleFix":"// before\nout, err := util.StructToMap(cfgPtr) // cfgPtr is nil or *Config -> **Config\n// after\nif cfgPtr != nil {\n    out, err := util.StructToMap(*cfgPtr)\n}","handlingStrategy":"type-guard","validationCode":"func canConvertToMap(in any) bool {\n    v := reflect.ValueOf(in)\n    if v.Kind() == reflect.Ptr {\n        if v.IsNil() { return false }\n        v = v.Elem()\n    }\n    return v.Kind() == reflect.Struct\n}","typeGuard":"func isStructLike(in any) bool {\n    v := reflect.ValueOf(in)\n    for v.Kind() == reflect.Ptr {\n        if v.IsNil() { return false }\n        v = v.Elem()\n    }\n    return v.Kind() == reflect.Struct\n}","tryCatchPattern":"if !isStructLike(in) {\n    return nil, fmt.Errorf(\"StructToMap requires struct, got %T\", in)\n}\nout, err := util.StructToMap(in)\nif err != nil {\n    return nil, err\n}","preventionTips":["Check for nil pointers before calling StructToMap","Never pass **T; dereference once first","Constrain generic helpers with interface{ constraints } or runtime kind checks"],"tags":["reflection","invalid-input","go"],"backgroundTag":"invalid-argument-value","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}