OpenNHP/opennhp · error

unsupported field type

Error message

unsupported field type: %v in slice

What it means

Inside marshal, a slice-typed field is only supported if its element kind is Uint8 (encoded as raw bytes) or, via the preceding branch, an array/struct of supported element types. Any other slice element kind (e.g. []int32, []string, [][]byte) has no defined wire encoding in this format, so the serializer rejects it with this error.

Solutions

  1. Change the field to []byte and encode the data yourself (e.g. with encoding/binary or json.Marshal before storing).
  2. Replace []T (struct T) with a struct containing fixed-size fields, or encode each element into a byte slice.
  3. Keep slices out of ztdo structs; store variable-length data in the metadata section instead.
  4. Pre-encode the collection into a single []byte field before calling marshal/toBuffer.

Example fix

// before
type H struct {
    Tags []string // unsupported
}
// after
type H struct {
    TagsBytes []byte // json.Marshal(tags) beforehand
}
Defensive patterns

Strategy: type-guard

Validate before calling

func sliceOK(t reflect.Type) bool {
    if t.Kind() != reflect.Slice { return true }
    return t.Elem().Kind() == reflect.Uint8 || t.Elem().Kind() == reflect.Struct
}

Type guard

// fail fast at init
t := reflect.TypeOf(MyHeader{})
for i := 0; i < t.NumField(); i++ {
    if ft := t.Field(i).Type; ft.Kind() == reflect.Slice && ft.Elem().Kind() != reflect.Uint8 {
        panic("ztdo field " + t.Field(i).Name + " must be []byte")
    }
}

Try / catch

if err := marshal(buf, s); err != nil {
    if strings.Contains(err.Error(), "unsupported field type") {
        return fmt.Errorf("field in %T has no ztdo encoding: pre-encode to []byte", s)
    }
    return err
}

Prevention

When it happens

Trigger: Marshaling a struct containing a slice field whose element type is not uint8 and not handled by the struct/array branch — e.g. []int, []string, []float64, or a slice of structs containing unsupported nested types.

Common situations: Adding a convenience field like Tags []string or Scores []float64 to a ztdo header/metadata struct; refactoring a fixed-size array field into a slice; reusing a JSON-oriented struct directly for ztdo serialization.

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/6447748b7a82d47a. Report an issue: GitHub.

Appendix: source

Thrown at nhp/core/ztdo/ztdo.go:515

			if err := marshal(buf, field.Interface()); err != nil {
				return err
			}
		} 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)

View on GitHub (pinned to 6e04ca5ff0)