hyperledger/fabric · error
receiver %T.%s does not return a an implementor of proto.Mes
Error message
receiver %T.%s does not return a an implementor of proto.Message as its first return value
What it means
Dispatch requires the first of the method's two return values to implement proto.Message (google.golang.org/protobuf), because it marshals that value into the response bytes. This error is thrown when the first return type does not implement proto.Message.
Source
Thrown at core/dispatcher/dispatcher.go:48
if method == (reflect.Value{}) {
return nil, errors.Errorf("receiver %T.%s does not exist", receiver, methodName)
}
if method.Type().NumIn() != 1 {
return nil, errors.Errorf("receiver %T.%s has %d parameters but expected 1", receiver, methodName, method.Type().NumIn())
}
inputType := method.Type().In(0)
if inputType.Kind() != reflect.Pointer {
return nil, errors.Errorf("receiver %T.%s does not accept a pointer as its argument", receiver, methodName)
}
if method.Type().NumOut() != 2 {
return nil, errors.Errorf("receiver %T.%s returns %d values but expected 2", receiver, methodName, method.Type().NumOut())
}
if !method.Type().Out(0).Implements(reflect.TypeFor[proto.Message]()) {
return nil, errors.Errorf("receiver %T.%s does not return a an implementor of proto.Message as its first return value", receiver, methodName)
}
if !method.Type().Out(1).Implements(reflect.TypeFor[error]()) {
return nil, errors.Errorf("receiver %T.%s does not return an error as its second return value", receiver, methodName)
}
inputValue := reflect.New(inputType.Elem())
inputMsg, ok := inputValue.Interface().(proto.Message)
if !ok {
return nil, errors.Errorf("receiver %T.%s does not accept a proto.Message as its argument, it is '%T'", receiver, methodName, inputValue.Interface())
}
err := d.Protobuf.Unmarshal(inputBytes, inputMsg)
if err != nil {
return nil, errors.WithMessagef(err, "could not decode input arg for %T.%s", receiver, methodName)
}
outputVals := method.Call([]reflect.Value{inputValue})View on GitHub (pinned to 2736b63f8f)
Solutions
- Change the first return value to a generated protobuf message type (*pb.SomeResponse).
- Regenerate proto types with the same protobuf-go module (google.golang.org/protobuf) the dispatcher uses; remove gogo/protobuf or golang/protobuf mixing.
- If a non-proto result is needed, serialize it into a field of a proto response wrapper message.
Example fix
// before
func (s *Svc) GetAsset(req *pb.GetRequest) (*Asset, error) { ... } // plain struct
// after
func (s *Svc) GetAsset(req *pb.GetRequest) (*pb.Asset, error) { ... } // generated proto type Defensive patterns
Strategy: validation
Validate before calling
func validateFirstReturnIsProto(receiver any, methodName string) error {
m := reflect.ValueOf(receiver).MethodByName(methodName)
if !m.IsValid() || m.Type().NumOut() != 2 {
return fmt.Errorf("method %s missing or wrong shape", methodName)
}
if !m.Type().Out(0).Implements(reflect.TypeFor[proto.Message]()) {
return fmt.Errorf("method %s first return does not implement proto.Message", methodName)
}
return nil
} Type guard
func firstReturnIsProtoMessage(receiver any, methodName string) bool {
m := reflect.ValueOf(receiver).MethodByName(methodName)
return m.IsValid() && m.Type().NumOut() >= 1 &&
m.Type().Out(0).Implements(reflect.TypeFor[proto.Message]())
} Prevention
- Only return protoc-gen-go generated types from handlers.
- Keep the proto runtime consistent: google.golang.org/protobuf everywhere; avoid gogo/protobuf or legacy golang/protobuf mixing.
- Add interface assertions in tests: var _ proto.Message = (*pb.MyResponse)(nil).
When it happens
Trigger: Calling Dispatch (via Invoke) on a method whose first return is a plain struct, string, []byte, or other non-proto type, e.g. func (s *Svc) Do(req *pb.Req) (MyResult, error).
Common situations: Handlers returning application-defined structs instead of generated protobuf types; mixing gogo/protobuf or legacy golang/protobuf types with the google.golang.org/protobuf runtime the dispatcher imports, so Implements(proto.Message) fails across mismatched runtimes.
Related errors
- receiver %T.%s has %d parameters but expected 1
- receiver %T.%s does not accept a pointer as its argument
- receiver %T.%s returns %d values but expected 2
- receiver %T.%s does not return an error as its second return
- server returned response of unexpected type: %v
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/b096ccc45792a2dc.
Report an issue: GitHub.