OpenNHP/opennhp · error
unsupported field type
Error message
unsupported field type: %v
What it means
marshal supports only struct fields and (for slices) uint8-element slices; a field whose kind is anything else — map, chan, func, interface, string at top level of the field type, etc. — has no encoding in this binary format and is rejected with this error at the outer else branch.
Solutions
- Remove the unsupported field from the struct or keep it unexported and store its encoded form in a []byte field.
- Replace maps/chans/interfaces with explicit fixed-size fields or byte slices.
- Split the struct: serialize only the wire-format portion with toBuffer, keep the rest in application memory.
- Consult the marshal implementation's supported kinds and mirror them in your struct design.
Example fix
// before
type H struct {
mu sync.Mutex // unsupported field kind
}
// after
type H struct {
// no locks/maps on the wire struct
Flags uint64
} Defensive patterns
Strategy: type-guard
Validate before calling
allowed := map[reflect.Kind]bool{reflect.Struct: true, reflect.Uint8: true, reflect.Slice: true, reflect.Array: true /* plus other supported fixed-size kinds */}
rv := reflect.TypeOf(MyStruct{})
for i := 0; i < rv.NumField(); i++ {
if !allowed[rv.Field(i).Type.Kind()] {
panic("unsupported ztdo field kind: " + rv.Field(i).Name)
}
} Type guard
k := reflect.TypeOf(field).Kind()
if !allowed[k] { return fmt.Errorf("field kind %v not ztdo-serializable", k) } Try / catch
if err := toBuffer(s); err != nil {
if strings.Contains(err.Error(), "unsupported field type") {
return fmt.Errorf("%T contains a field kind without a wire encoding", s)
}
return err
} Prevention
- Keep wire structs free of maps, chans, funcs, interfaces and mutexes
- Separate transport structs from application structs
- Add a unit test that round-trips every wire struct through toBuffer/toStructure
When it happens
Trigger: Marshaling a struct with a field of kind map, chan, func, interface, complex, or an unsafe/unsupported kind; also triggered when a non-slice, non-struct field type falls through all supported branches.
Common situations: Leaving a debug/logger or sync.Mutex field on a struct passed to toBuffer; embedding a map for extra attributes; passing structs containing time.Time in a codebase where the expected pattern is fixed-size fields (time.Time is a struct and recurses into its unexported fields, often failing deeper).
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- unsupported field type
- data must be a struct
- unsupported element type
- failed to marshal evidence
- data must be a pointer
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/9c98885b0b827003.
Report an issue: GitHub.
Appendix: source
Thrown at nhp/core/ztdo/ztdo.go:518
} else {
var bytes []byte
if field.Type().Kind() == reflect.Array { // here assume it's an array of byte.
bytes = make([]byte, field.Len())
reflect.Copy(reflect.ValueOf(bytes), field)
} else if field.Type().Kind() == reflect.Slice {
if field.Type().Elem().Kind() == reflect.Struct {
for j := range field.Len() {
if err := marshal(buf, field.Index(j).Interface()); err != nil {
return err
}
}
} else if field.Type().Elem().Kind() == reflect.Uint8 {
bytes = field.Interface().([]byte)
} else {
return fmt.Errorf("unsupported field type: %v in slice", field.Type().Elem().Kind())
}
} else {
return fmt.Errorf("unsupported field type: %v", field.Type().Kind())
}
if _, err := buf.Write(bytes); err != nil {
return err
}
}
}
return nil
}
// unmarshal recursively deserializes binary data from a file into a Go struct
func unmarshal(f *os.File, data any) error {
rValues := reflect.ValueOf(data)
rTypes := reflect.TypeOf(data)
if rValues.Kind() == reflect.Pointer {
rTypes = rTypes.Elem()
rValues = rValues.Elem()View on GitHub (pinned to 6e04ca5ff0)