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

  1. 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.
  2. Change the struct field type to match the incoming data (e.g. int -> float64, string -> []string) or add a custom type with UnmarshalJSON.
  3. 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

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


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/9ac43518ae672564. Report an issue: GitHub.