{"record":{"id":"b837075a851d3d7d","repo":"hyperledger/fabric","slug":"receiver-t-s-does-not-return-an-error-as-its-sec","errorCode":null,"errorMessage":"receiver %T.%s does not return an error as its second return value","messagePattern":"receiver %T\\.(.+?) does not return an error as its second return value","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/dispatcher/dispatcher.go","lineNumber":52,"sourceCode":"\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 {\n\t\treturn nil, errors.WithMessagef(err, \"could not decode input arg for %T.%s\", receiver, methodName)\n\t}\n\n\toutputVals := method.Call([]reflect.Value{inputValue})\n\n\tif !outputVals[1].IsNil() {\n\t\treturn nil, outputVals[1].Interface().(error)\n\t}","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/dispatcher/dispatcher.go#L34-L70","documentation":"Dispatch requires the second of the method's two return values to implement the error interface so failures can be propagated to the caller. This error is thrown when the second return type does not implement error.","triggerScenarios":"Calling Dispatch (via Invoke) on a method like func (s *Svc) Do(req *pb.Req) (*pb.Resp, string) or (*pb.Resp, *MyErrStruct) where the second return type lacks an Error() string method.","commonSituations":"Handlers returning custom error structs that do not implement error; returning a status value or error code string directly; accidental type or order swaps after refactoring.","solutions":["Change the second return value to the standard error interface: func (s *Svc) Do(req *pb.Req) (*pb.Resp, error).","If a custom error type is kept, add an Error() string method so it implements error.","Move custom failure details into the response proto message and return a plain error instead."],"exampleFix":"// before\nfunc (s *Svc) Do(req *pb.Req) (*pb.Resp, *FailureInfo) { ... }\n\n// after\nfunc (s *Svc) Do(req *pb.Req) (*pb.Resp, error) {\n    ...\n    if failed {\n        return nil, fmt.Errorf(\"failed: %v\", info)\n    }\n    return resp, nil\n}","handlingStrategy":"validation","validationCode":"func validateSecondReturnIsError(receiver any, methodName string) error {\n    m := reflect.ValueOf(receiver).MethodByName(methodName)\n    if !m.IsValid() || m.Type().NumOut() != 2 {\n        return fmt.Errorf(\"method %s missing or wrong shape\", methodName)\n    }\n    if !m.Type().Out(1).Implements(reflect.TypeFor[error]()) {\n        return fmt.Errorf(\"method %s second return does not implement error\", methodName)\n    }\n    return nil\n}","typeGuard":"func secondReturnIsError(receiver any, methodName string) bool {\n    m := reflect.ValueOf(receiver).MethodByName(methodName)\n    return m.IsValid() && m.Type().NumOut() >= 2 &&\n        m.Type().Out(1).Implements(reflect.TypeFor[error]())\n}","tryCatchPattern":null,"preventionTips":["Always end handler signatures with a plain error return value.","If using custom error types, assert they implement error: var _ error = (*MyErr)(nil) — or simply return error.","Run a startup reflection check over all dispatched methods to catch signature drift early."],"tags":["go","reflection","error-handling","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"}