{"record":{"id":"48e5992e395a0496","repo":"hyperledger/fabric","slug":"unsupported-pointer-type-v-for-field-s-must-be","errorCode":null,"errorMessage":"unsupported pointer type %v for field %s (must be proto)","messagePattern":"unsupported pointer type (.+?) for field (.+?) \\(must be proto\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/chaincode/lifecycle/serializer.go","lineNumber":102,"sourceCode":"\tif value.Kind() != reflect.Struct {\n\t\treturn reflect.Value{}, nil, errors.Errorf(\"must be pointers to struct, but got pointer to %v\", value.Kind())\n\t}\n\n\tallFields := make([]string, value.NumField())\n\tfor i := 0; i < value.NumField(); i++ {\n\t\tfieldName := value.Type().Field(i).Name\n\t\tfieldValue := value.Field(i)\n\t\tallFields[i] = fieldName\n\t\tswitch fieldValue.Kind() {\n\t\tcase reflect.String:\n\t\tcase reflect.Int64:\n\t\tcase reflect.Slice:\n\t\t\tif fieldValue.Type().Elem().Kind() != reflect.Uint8 {\n\t\t\t\treturn reflect.Value{}, nil, errors.Errorf(\"unsupported slice type %v for field %s\", fieldValue.Type().Elem().Kind(), fieldName)\n\t\t\t}\n\t\tcase reflect.Pointer:\n\t\t\tif !fieldValue.Type().Implements(ProtoMessageType) {\n\t\t\t\treturn reflect.Value{}, nil, errors.Errorf(\"unsupported pointer type %v for field %s (must be proto)\", fieldValue.Type().Elem(), fieldName)\n\t\t\t}\n\t\tdefault:\n\t\t\treturn reflect.Value{}, nil, errors.Errorf(\"unsupported structure field kind %v for serialization for field %s\", fieldValue.Kind(), fieldName)\n\t\t}\n\t}\n\treturn value, allFields, nil\n}\n\n// Serialize takes a pointer to a struct, and writes each of its fields as keys\n// into a namespace.  It also writes the struct metadata (if it needs updating)\n// and,  deletes any keys in the namespace which are not found in the struct.\n// Note: If a key already exists for the field, and the value is unchanged, then\n// the key is _not_ written to.\nfunc (s *Serializer) Serialize(namespace, name string, structure any, state ReadWritableState) error {\n\tvalue, allFields, err := s.SerializableChecks(structure)\n\tif err != nil {\n\t\treturn errors.WithMessagef(err, \"structure for namespace %s/%s is not serializable\", namespace, name)\n\t}","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/chaincode/lifecycle/serializer.go#L84-L120","documentation":"Pointer fields in lifecycle state structs must implement proto.Message. If a field is a pointer to something else (e.g. *string, *int64, custom struct pointer), SerializableChecks fails with this error naming the element type and field.","triggerScenarios":"Struct field like *Config or *int64 passed through Serialize/Deserialize/IsSerialized where *Config does not implement proto.Message.","commonSituations":"Adding a pointer to an internal Go struct or optional scalar to a chaincode definition/metadata struct; partially converting a struct to protobuf style where one field was left as a plain pointer.","solutions":["Make the field a protobuf message pointer (generate from a .proto so it implements proto.Message)","Change the field to a non-pointer supported kind (string, int64, []byte) if no proto is needed","Nil-check semantics: Serialize stores nil bytes for nil proto pointers, which is supported — only non-proto pointers are rejected"],"exampleFix":"// before\ntype Def struct { Policy *MyGoPolicy }\n// after\ntype Def struct { Policy *pb.ApplicationPolicy } // implements proto.Message","handlingStrategy":"type-guard","validationCode":"func allPointersAreProto(v any) error {\n  t := reflect.TypeOf(v).Elem()\n  for i := 0; i < t.NumField(); i++ {\n    f := t.Field(i).Type\n    if f.Kind() == reflect.Pointer && !f.Implements(reflect.TypeFor[proto.Message]()) {\n      return fmt.Errorf(\"field %s pointer is not proto.Message\", t.Field(i).Name)\n    }\n  }\n  return nil\n}","typeGuard":"func isProtoPointer(f reflect.Type) bool { return f.Kind() == reflect.Pointer && f.Implements(reflect.TypeFor[proto.Message]()) }","tryCatchPattern":"_, _, err := s.SerializableChecks(input)\nif err != nil && strings.Contains(err.Error(), \"must be proto\") {\n  // convert the field to a proto message pointer or supported kind\n}","preventionTips":["Use generated protobuf types for pointer fields","Avoid optional *string/*int64 fields in state structs","Compile-time assert: var _ proto.Message = (*pb.MyMsg)(nil)"],"tags":["fabric","serialization","protobuf","unsupported-type"],"backgroundTag":"non-proto-pointer-field","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}