hyperledger/fabric · error
receiver %T.%s has %d parameters but expected 1
Error message
receiver %T.%s has %d parameters but expected 1
What it means
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.
Source
Thrown at core/dispatcher/dispatcher.go:35
// so that the receiver may focus on the implementation details rather than the proto hassles.
type Dispatcher struct {
// Protobuf should pass through to Google Protobuf in production paths
Protobuf Protobuf
}
// Dispatch deserializes the input bytes to the correct type for the method in the receiver, then
// if successful, marshals the output message to bytes and returns it. On error, it simply returns
// the error. The method on the receiver must take a single parameter which is a concrete proto
// message type and it should return a proto message and error.
func (d *Dispatcher) Dispatch(inputBytes []byte, methodName string, receiver any) ([]byte, error) {
method := reflect.ValueOf(receiver).MethodByName(methodName)
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)
}View on GitHub (pinned to 2736b63f8f)
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.
Example fix
// before
func (s *Chaincode) CreateAsset(ctx context.Context, req *pb.CreateRequest) (*pb.Response, error) { ... }
// after
func (s *Chaincode) CreateAsset(req *pb.CreateRequest) (*pb.Response, error) {
ctx := s.ctx // dependency held by the receiver
...
} Defensive patterns
Strategy: validation
Validate before calling
func validateMethodArity(receiver any, methodName string) error {
m := reflect.ValueOf(receiver).MethodByName(methodName)
if !m.IsValid() {
return fmt.Errorf("method %s does not exist", methodName)
}
if m.Type().NumIn() != 1 {
return fmt.Errorf("method %s has %d params, want 1", methodName, m.Type().NumIn())
}
return nil
}
// call at startup before dispatching
if err := validateMethodArity(&MySvc{}, "Do"); err != nil { panic(err) } Type guard
func hasSingleParam(receiver any, methodName string) bool {
m := reflect.ValueOf(receiver).MethodByName(methodName)
return m.IsValid() && m.Type().NumIn() == 1
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- 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 a an implementor of proto.Mes
- receiver %T.%s does not return an error as its second return
- message of type %s unknown
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/5dd9272d16154dc5.
Report an issue: GitHub.