{"record":{"id":"1fb04acde7c92aa7","repo":"micro/go-micro","slug":"subscriber-v-v-takes-wrong-number-of-args-v-re","errorCode":null,"errorMessage":"subscriber %v.%v takes wrong number of args: %v required signature %s","messagePattern":"subscriber (.+?)\\.(.+?) takes wrong number of args: (.+?) required signature (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/grpc/subscriber.go","lineNumber":148,"sourceCode":"\t\tif typ.NumOut() != 1 {\n\t\t\treturn fmt.Errorf(\"subscriber %v has wrong number of outs: %v require signature %s\",\n\t\t\t\tname, typ.NumOut(), subSig)\n\t\t}\n\t\tif returnType := typ.Out(0); returnType != typeOfError {\n\t\t\treturn fmt.Errorf(\"subscriber %v returns %v not error\", name, returnType.String())\n\t\t}\n\t} else {\n\t\thdlr := reflect.ValueOf(sub.Subscriber())\n\t\tname := reflect.Indirect(hdlr).Type().Name()\n\n\t\tfor m := 0; m < typ.NumMethod(); m++ {\n\t\t\tmethod := typ.Method(m)\n\n\t\t\tswitch method.Type.NumIn() {\n\t\t\tcase 3:\n\t\t\t\targType = method.Type.In(2)\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"subscriber %v.%v takes wrong number of args: %v required signature %s\",\n\t\t\t\t\tname, method.Name, method.Type.NumIn(), subSig)\n\t\t\t}\n\n\t\t\tif !isExportedOrBuiltinType(argType) {\n\t\t\t\treturn fmt.Errorf(\"%v argument type not exported: %v\", name, argType)\n\t\t\t}\n\t\t\tif method.Type.NumOut() != 1 {\n\t\t\t\treturn fmt.Errorf(\n\t\t\t\t\t\"subscriber %v.%v has wrong number of outs: %v require signature %s\",\n\t\t\t\t\tname, method.Name, method.Type.NumOut(), subSig)\n\t\t\t}\n\t\t\tif returnType := method.Type.Out(0); returnType != typeOfError {\n\t\t\t\treturn fmt.Errorf(\"subscriber %v.%v returns %v not error\", name, method.Name, returnType.String())\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/server/grpc/subscriber.go#L130-L166","documentation":"For struct-based subscribers, every exported method is validated as a handler and must take exactly 3 inputs: receiver, context.Context, and the message (subSig func(context.Context, interface{}) error plus the receiver). validateSubscriber iterates typ.NumMethod() and fails on the first method whose NumIn() != 3, including methods you may not have intended as subscribers.","triggerScenarios":"Registering a struct via Subscribe(topic, handlerStruct) where any exported method has the wrong arity, e.g. methods with only (msg *T) error (NumIn 2), or helper/cleanup/exported utility methods with extra parameters (NumIn 4+).","commonSituations":"Adding a context parameter or extra option to one method on the handler struct; leaving exported helper methods (Health, Close, Configure) on the same struct; value-receiver vs pointer-receiver confusion changing the method set; upgrading go-micro and getting stricter validation than before.","solutions":["Give every exported method on the registered struct the signature (ctx context.Context, msg *T) error.","Move non-subscriber helpers off the struct or make them unexported (lowercase) so NumMethod doesn't see them.","Register a dedicated subscriber struct containing only handler methods, separate from your service struct.","If the handler needs extra config, capture it in the struct's fields (set at construction), not as method parameters."],"exampleFix":"// before\ntype Handlers struct{}\nfunc (h *Handlers) Handle(ctx context.Context, e *pb.Event, opts ...Option) error { ... }\n// after\ntype Handlers struct{ opts []Option }\nfunc (h *Handlers) Handle(ctx context.Context, e *pb.Event) error { ... }","handlingStrategy":"validation","validationCode":"func validSubscriberStruct(h interface{}) error {\n\tt := reflect.TypeOf(h)\n\tctxType := reflect.TypeOf((*context.Context)(nil)).Elem()\n\terrType := reflect.TypeOf((*error)(nil)).Elem()\n\tfor i := 0; i < t.NumMethod(); i++ {\n\t\tm := t.Method(i)\n\t\tif m.Type.NumIn() != 3 || !m.Type.In(1).Implements(ctxType) || m.Type.NumOut() != 1 || m.Type.Out(0) != errType {\n\t\t\treturn fmt.Errorf(\"method %s does not match func(ctx context.Context, msg T) error\", m.Name)\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep handler structs dedicated to subscription methods only.","Make helpers unexported so reflect.NumMethod skips them.","Every exported method must be (ctx context.Context, msg *T) error.","Run validSubscriberStruct on all handler structs in a startup/unit test before subscribing."],"tags":["go","grpc","subscriber","reflection","handler-signature","struct-methods"],"backgroundTag":"invalid-handler-signature","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}