wavetermdev/waveterm · error

cannot set value of type %v to field of type %v

Error message

cannot set value of type %v to field of type %v

What it means

setValue is the reflection helper behind MapToStruct. After direct assignment, assignability, pointer-addressing, and convertibility checks all fail, it reports that the value's dynamic type cannot be stored in the destination field type. Structs with same-layout mismatched types (e.g. named string types in different packages, map into slice) land here.

Source

Thrown at tsunami/util/marshal.go:120

	// Check if types are assignable
	if valueRef.Type().AssignableTo(field.Type()) {
		field.Set(valueRef)
		return nil
	}

	// If field is pointer and value isn't already a pointer, try address
	if field.Kind() == reflect.Ptr && valueRef.Kind() != reflect.Ptr {
		return setValue(field, valueRef.Addr().Interface())
	}

	// Try conversion if types are convertible
	if valueRef.Type().ConvertibleTo(field.Type()) {
		field.Set(valueRef.Convert(field.Type()))
		return nil
	}

	return fmt.Errorf("cannot set value of type %v to field of type %v", valueRef.Type(), field.Type())
}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Align the map value's type with the field type before calling MapToStruct (convert slices/maps explicitly).
  2. Change the struct field to a type compatible with JSON-decoded shapes (e.g. []any then convert, or a custom UnmarshalJSON type).
  3. If the types should be convertible but aren't, check for named-type/kind mismatches and add an explicit conversion step.

Example fix

// before
m := map[string]any{"tags": []any{"a", "b"}}
util.MapToStruct(m, &cfg) // Tags []string fails
// after
m["tags"] = []string{"a", "b"}
util.MapToStruct(m, &cfg)
Defensive patterns

Strategy: validation

Validate before calling

func setValueSafe(field reflect.Value, value any) (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("cannot set %T into %s", value, field.Type())
        }
    }()
    if value != nil && reflect.TypeOf(value).ConvertibleTo(field.Type()) {
        field.Set(reflect.ValueOf(value).Convert(field.Type()))
    }
    return nil
}

Type guard

func fieldCompatible(value any, field reflect.Value) bool {
    if value == nil { return true }
    vt := reflect.TypeOf(value)
    return vt == field.Type() || vt.AssignableTo(field.Type()) || vt.ConvertibleTo(field.Type())
}

Try / catch

if err := util.MapToStruct(in, &out); err != nil {
    if strings.Contains(err.Error(), "cannot set value of type") {
        return normalizeAndRetry(in, &out) // coerce types then retry once
    }
    return err
}

Prevention

When it happens

Trigger: MapToStruct encounters a value whose type is neither equal, assignable, addressable-for-pointer, nor convertible to the field type: e.g. map[string]any into a []string field, json.Number into time.Time, two distinct named types with different underlying kinds.

Common situations: Round-tripping data through JSON (everything becomes float64/string/bool/[]any/map[string]any) into structs with typed slices, nested structs, or time.Time fields; refactoring a field type without updating producers.

Related errors


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