wavetermdev/waveterm · error

input must be a struct or pointer to struct, got %v

Error message

input must be a struct or pointer to struct, got %v

What it means

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.

Source

Thrown at tsunami/util/marshal.go:57

			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())
	}

	// Get type information
	typ := val.Type()
	out := make(map[string]any)

	// 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)
		out[name] = val.Field(i).Interface()
	}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Ensure you pass a struct or a single-level pointer to a struct: util.StructToMap(&myStruct).
  2. Check for nil before calling: if ptr == nil { ... } — nil pointers cannot be converted.
  3. If you have a **T or interface{}, unwrap to the struct first with reflection or a type assertion.

Example fix

// before
out, err := util.StructToMap(cfgPtr) // cfgPtr is nil or *Config -> **Config
// after
if cfgPtr != nil {
    out, err := util.StructToMap(*cfgPtr)
}
Defensive patterns

Strategy: type-guard

Validate before calling

func canConvertToMap(in any) bool {
    v := reflect.ValueOf(in)
    if v.Kind() == reflect.Ptr {
        if v.IsNil() { return false }
        v = v.Elem()
    }
    return v.Kind() == reflect.Struct
}

Type guard

func isStructLike(in any) bool {
    v := reflect.ValueOf(in)
    for v.Kind() == reflect.Ptr {
        if v.IsNil() { return false }
        v = v.Elem()
    }
    return v.Kind() == reflect.Struct
}

Try / catch

if !isStructLike(in) {
    return nil, fmt.Errorf("StructToMap requires struct, got %T", in)
}
out, err := util.StructToMap(in)
if err != nil {
    return nil, err
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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