{"record":{"id":"5dd9272d16154dc5","repo":"hyperledger/fabric","slug":"receiver-t-s-has-d-parameters-but-expected-1","errorCode":null,"errorMessage":"receiver %T.%s has %d parameters but expected 1","messagePattern":"receiver %T\\.(.+?) has (.+?) parameters but expected 1","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/dispatcher/dispatcher.go","lineNumber":35,"sourceCode":"// so that the receiver may focus on the implementation details rather than the proto hassles.\ntype Dispatcher struct {\n\t// Protobuf should pass through to Google Protobuf in production paths\n\tProtobuf Protobuf\n}\n\n// Dispatch deserializes the input bytes to the correct type for the method in the receiver, then\n// if successful, marshals the output message to bytes and returns it.  On error, it simply returns\n// the error.  The method on the receiver must take a single parameter which is a concrete proto\n// 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}","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/dispatcher/dispatcher.go#L17-L53","documentation":"Dispatch validates via reflection that the receiver method named methodName has exactly one input parameter (a pointer to a proto message), so it knows what type to unmarshal the input bytes into. This error is thrown when the method's reflect.Type reports NumIn() != 1, meaning the dispatcher cannot build the required argument. It is a handler-contract violation caught before any call is made.","triggerScenarios":"Calling Dispatcher.Dispatch (via Invoke) on a method with zero parameters, two or more parameters (e.g. func (s *Svc) Do(ctx context.Context, req *pb.Req)), or a variadic signature.","commonSituations":"Handlers written in gRPC style with a leading context.Context parameter; helper methods with extra config/db parameters being reused as dispatch targets; methods looked up on a value receiver so the receiver counts as In(0).","solutions":["Change the method to take exactly one parameter: a pointer to a concrete proto message, e.g. func (s *Svc) Do(req *pb.Request) (*pb.Response, error).","Move context, config, and dependencies into fields of the receiver struct instead of method parameters.","Ensure MethodByName is resolved on the pointer receiver (&Svc{}) if the method has a pointer receiver, so the receiver is not counted as an input.","If extra inputs are needed, wrap the logic in a single-parameter adapter method."],"exampleFix":"// before\nfunc (s *Chaincode) CreateAsset(ctx context.Context, req *pb.CreateRequest) (*pb.Response, error) { ... }\n\n// after\nfunc (s *Chaincode) CreateAsset(req *pb.CreateRequest) (*pb.Response, error) {\n    ctx := s.ctx // dependency held by the receiver\n    ...\n}","handlingStrategy":"validation","validationCode":"func validateMethodArity(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().NumIn() != 1 {\n        return fmt.Errorf(\"method %s has %d params, want 1\", methodName, m.Type().NumIn())\n    }\n    return nil\n}\n// call at startup before dispatching\nif err := validateMethodArity(&MySvc{}, \"Do\"); err != nil { panic(err) }","typeGuard":"func hasSingleParam(receiver any, methodName string) bool {\n    m := reflect.ValueOf(receiver).MethodByName(methodName)\n    return m.IsValid() && m.Type().NumIn() == 1\n}","tryCatchPattern":null,"preventionTips":["Follow the single-pointer-proto-arg contract: func (s *T) M(req *pb.Req) (*pb.Resp, error).","Hold dependencies (ctx, db, config) as receiver struct fields, not method parameters.","Write a startup unit test that reflect-validates every dispatched method.","Resolve methods via the pointer receiver (&T{}) so the receiver is not counted as an input."],"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"}