wavetermdev/waveterm · error
error setting field %s: %w
Error message
error setting field %s: %w
What it means
MapToStruct copies values from a map[string]any into struct fields via reflection. This error wraps the underlying setValue failure (type mismatch, unassignable/convertible types) with the JSON field name that failed, so you know exactly which key in the map could not be mapped into the destination struct.
Source
Thrown at tsunami/util/marshal.go:40
return fmt.Errorf("out parameter must be a pointer to struct, got pointer to %v", elem.Kind())
}
// Get type information
typ := elem.Type()
// For each field in the struct
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
// Skip unexported fields
if !field.IsExported() {
continue
}
name := getJSONName(field)
if value, ok := in[name]; ok {
if err := setValue(elem.Field(i), value); err != nil {
return fmt.Errorf("error setting field %s: %w", name, err)
}
}
}
return nil
}
func StructToMap(in any) (map[string]any, error) {
// Get value and handle pointer
val := reflect.ValueOf(in)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
// Check that we have a struct
if val.Kind() != reflect.Struct {
return nil, fmt.Errorf("input must be a struct or pointer to struct, got %v", val.Kind())
}View on GitHub (pinned to a4447c1563)
Solutions
- Read the wrapped field name in the error and fix the value's type in the map (or use encoding/json.Unmarshal, which has richer unmarshaling rules) so it matches the struct field type.
- Change the struct field type to match the incoming data (e.g. int -> float64, string -> []string) or add a custom type with UnmarshalJSON.
- Pre-normalize the map before calling MapToStruct (coerce numbers with FromFloat64-style helpers, convert nested maps).
Example fix
// before
var cfg Config
err := util.MapToStruct(map[string]any{"port": "8080"}, &cfg) // error setting field port
// after
port, _ := strconv.Atoi("8080")
err := util.MapToStruct(map[string]any{"port": port}, &cfg) Defensive patterns
Strategy: try-catch
Validate before calling
func validateMapping(in map[string]any, out any) error {
v := reflect.ValueOf(out).Elem().Type()
for i := 0; i < v.NumField(); i++ {
name := v.Field(i).Name
if tag := v.Field(i).Tag.Get("json"); tag != "" && tag != "-" {
name = strings.Split(tag, ",")[0]
}
if val, ok := in[name]; ok && val != nil {
if reflect.TypeOf(val) != reflect.TypeOf(v.Field(i)) {
return fmt.Errorf("field %s: value type %T may not map to %s", name, val, v.Field(i).Type)
}
}
}
return nil
} Try / catch
if err := util.MapToStruct(in, &cfg); err != nil {
var fieldErr string
if _, scanErr := fmt.Sscanf(err.Error(), "error setting field %s", &fieldErr); scanErr == nil {
log.Printf("bad value for field %q, using defaults", fieldErr)
}
return fmt.Errorf("config mapping failed: %w", err)
} Prevention
- Prefer encoding/json.Unmarshal which coerces more JSON shapes, unless you need map-to-struct specifically
- Keep map values sourced from the same schema version as the struct definition
- Write a round-trip test: StructToMap -> MapToStruct -> compare, to catch type drift early
When it happens
Trigger: Call util.MapToStruct(in, &outStruct) where in contains a key matching a struct field's json tag (or Go field name), but the value's dynamic type is not assignable or convertible to the field's type (e.g. string into int, []any into []string, nested map into a non-struct field).
Common situations: Decoding loosely-typed config (YAML/TOML parsed as map[string]any) into typed structs; JSON numbers arriving as float64 into int fields after a numeric-type change; schema drift where an API returns a string but the struct expects a slice or struct.
Related errors
- error setting field %s: %w
- cannot set value of type %v to field of type %v
- out parameter must be a pointer, got %v
- out parameter must be a pointer to struct, got pointer to %v
- input must be a struct or pointer to struct, got %v
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/9ac43518ae672564.
Report an issue: GitHub.