{"record":{"id":"83f8986cddc163f8","repo":"hyperledger/fabric","slug":"receiver-t-s-returns-d-values-but-expected-2","errorCode":null,"errorMessage":"receiver %T.%s returns %d values but expected 2","messagePattern":"receiver %T\\.(.+?) returns (.+?) values but expected 2","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/dispatcher/dispatcher.go","lineNumber":44,"sourceCode":"// message type and it should return a proto message and error.\nfunc (d *Dispatcher) Dispatch(inputBytes []byte, methodName string, receiver any) ([]byte, error) {\n\tmethod := reflect.ValueOf(receiver).MethodByName(methodName)\n\n\tif method == (reflect.Value{}) {\n\t\treturn nil, errors.Errorf(\"receiver %T.%s does not exist\", receiver, methodName)\n\t}\n\n\tif method.Type().NumIn() != 1 {\n\t\treturn nil, errors.Errorf(\"receiver %T.%s has %d parameters but expected 1\", receiver, methodName, method.Type().NumIn())\n\t}\n\n\tinputType := method.Type().In(0)\n\tif inputType.Kind() != reflect.Pointer {\n\t\treturn nil, errors.Errorf(\"receiver %T.%s does not accept a pointer as its argument\", receiver, methodName)\n\t}\n\n\tif method.Type().NumOut() != 2 {\n\t\treturn nil, errors.Errorf(\"receiver %T.%s returns %d values but expected 2\", receiver, methodName, method.Type().NumOut())\n\t}\n\n\tif !method.Type().Out(0).Implements(reflect.TypeFor[proto.Message]()) {\n\t\treturn nil, errors.Errorf(\"receiver %T.%s does not return a an implementor of proto.Message as its first return value\", receiver, methodName)\n\t}\n\n\tif !method.Type().Out(1).Implements(reflect.TypeFor[error]()) {\n\t\treturn nil, errors.Errorf(\"receiver %T.%s does not return an error as its second return value\", receiver, methodName)\n\t}\n\n\tinputValue := reflect.New(inputType.Elem())\n\tinputMsg, ok := inputValue.Interface().(proto.Message)\n\tif !ok {\n\t\treturn nil, errors.Errorf(\"receiver %T.%s does not accept a proto.Message as its argument, it is '%T'\", receiver, methodName, inputValue.Interface())\n\t}\n\n\terr := d.Protobuf.Unmarshal(inputBytes, inputMsg)\n\tif err != nil {","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/dispatcher/dispatcher.go#L26-L62","documentation":"Dispatch's contract is that a receiver method returns exactly two values: a proto.Message first and an error second, so it can marshal the response and propagate failures. This error is thrown when reflect reports NumOut() != 2, so the dispatcher cannot map the returns to its standard (response, error) shape.","triggerScenarios":"Calling Dispatch (via Invoke) on a method returning only one value (e.g. just *pb.Response or just error), three values, or nothing at all.","commonSituations":"Handlers written like plain Go helpers returning only an error; returns in the wrong order like (error, *pb.Response); methods returning an extra bool or metadata value; ports of gRPC handlers with additional return values.","solutions":["Change the method to return exactly (protoMessage, error): func (s *Svc) Do(req *pb.Req) (*pb.Resp, error).","If extra data must be returned, embed it as fields in the response proto message rather than adding another return value.","If there is nothing meaningful to return, return an empty proto response message plus a nil error."],"exampleFix":"// before\nfunc (s *Svc) DeleteAsset(req *pb.DeleteRequest) error { ... }\n\n// after\nfunc (s *Svc) DeleteAsset(req *pb.DeleteRequest) (*pb.DeleteResponse, error) {\n    ...\n    return &pb.DeleteResponse{}, nil\n}","handlingStrategy":"validation","validationCode":"func validateTwoReturns(receiver any, methodName string) error {\n    m := reflect.ValueOf(receiver).MethodByName(methodName)\n    if !m.IsValid() {\n        return fmt.Errorf(\"method %s does not exist\", methodName)\n    }\n    if m.Type().NumOut() != 2 {\n        return fmt.Errorf(\"method %s has %d returns, want (proto.Message, error)\", methodName, m.Type().NumOut())\n    }\n    return nil\n}","typeGuard":"func returnsMessageAndError(receiver any, methodName string) bool {\n    m := reflect.ValueOf(receiver).MethodByName(methodName)\n    if !m.IsValid() || m.Type().NumOut() != 2 {\n        return false\n    }\n    return m.Type().Out(0).Implements(reflect.TypeFor[proto.Message]()) &&\n        m.Type().Out(1).Implements(reflect.TypeFor[error]())\n}","tryCatchPattern":null,"preventionTips":["Standardize on the (proto message, error) return pair for every dispatched method.","Validate all handler signatures once at service startup with a reflection sweep.","Return auxiliary data inside the response proto message, not as extra return values."],"tags":["go","reflection","proto","contract-violation"],"backgroundTag":"handler-signature-mismatch","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"}