hyperledger/fabric · error
must be pointers to struct, but got pointer to %v
Error message
must be pointers to struct, but got pointer to %v
What it means
After confirming the argument is a pointer, SerializableChecks dereferences it and requires the pointee to be a struct, since field-by-field serialization only works on structs. A pointer to a non-struct (int, string, slice, etc.) triggers this error with the dereferenced kind.
Source
Thrown at core/chaincode/lifecycle/serializer.go:85
// to the same value again) custom serialization is required.
type Serializer struct {
// Marshaler, when nil uses the standard protobuf impl.
// Can be overridden for test.
Marshaler Marshaler
}
// SerializableChecks performs some boilerplate checks to make sure the given structure
// is serializable. It returns the reflected version of the value and a slice of all
// field names, or an error.
func (s *Serializer) SerializableChecks(structure any) (reflect.Value, []string, error) {
value := reflect.ValueOf(structure)
if value.Kind() != reflect.Pointer {
return reflect.Value{}, nil, errors.Errorf("must be pointer to struct, but got non-pointer %v", value.Kind())
}
value = value.Elem()
if value.Kind() != reflect.Struct {
return reflect.Value{}, nil, errors.Errorf("must be pointers to struct, but got pointer to %v", value.Kind())
}
allFields := make([]string, value.NumField())
for i := 0; i < value.NumField(); i++ {
fieldName := value.Type().Field(i).Name
fieldValue := value.Field(i)
allFields[i] = fieldName
switch fieldValue.Kind() {
case reflect.String:
case reflect.Int64:
case reflect.Slice:
if fieldValue.Type().Elem().Kind() != reflect.Uint8 {
return reflect.Value{}, nil, errors.Errorf("unsupported slice type %v for field %s", fieldValue.Type().Elem().Kind(), fieldName)
}
case reflect.Pointer:
if !fieldValue.Type().Implements(ProtoMessageType) {
return reflect.Value{}, nil, errors.Errorf("unsupported pointer type %v for field %s (must be proto)", fieldValue.Type().Elem(), fieldName)
}View on GitHub (pinned to 2736b63f8f)
Solutions
- Define a dedicated struct type whose fields hold the data, and pass a pointer to that struct
- Move scalar values into a struct field instead of serializing them directly
- Use a plain state PutState call for raw key/value data rather than the struct serializer
Example fix
// before
val := int64(42)
s.Serialize(state, metadata, &val)
// after
type Counter struct{ Value int64 }
s.Serialize(state, metadata, &Counter{Value: 42}) Defensive patterns
Strategy: type-guard
Validate before calling
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Pointer || rv.Elem().Kind() != reflect.Struct {
return errors.New("lifecycle serializer requires pointer-to-struct")
} Type guard
func isPointerToStruct(v any) bool {
rv := reflect.ValueOf(v)
return rv.Kind() == reflect.Pointer && rv.Elem().Kind() == reflect.Struct
} Try / catch
_, _, err := s.SerializableChecks(input)
if err != nil && strings.Contains(err.Error(), "pointers to struct") {
// input is *scalar/*slice — restructure into a struct
} Prevention
- Do not serialize raw scalars or slices via the struct serializer
- Define dedicated metadata structs for persisted state
- Use plain PutState/GetState for non-struct data
When it happens
Trigger: Calling Serialize/Deserialize/IsSerialized with *int, *string, *[]byte or any pointer-to-non-struct type.
Common situations: Trying to serialize scalar or slice values directly through the lifecycle serializer; generic helper code that wraps arbitrary types in pointers; test code checking serializer behavior.
Related errors
- must be pointer to struct, but got non-pointer %v
- proto: Marshal called with nil
- unsupported slice type %v for field %s
- unsupported pointer type %v for field %s (must be proto)
- unsupported structure field kind %v for serialization for fi
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/107601f8f6f0ea09.
Report an issue: GitHub.