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

  1. Remove the unsupported field from the struct or keep it unexported and store its encoded form in a []byte field.
  2. Replace maps/chans/interfaces with explicit fixed-size fields or byte slices.
  3. Split the struct: serialize only the wire-format portion with toBuffer, keep the rest in application memory.
  4. 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

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


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)