{"record":{"id":"22d7347e52be77dd","repo":"micro/go-micro","slug":"subscriber-v-argument-type-not-exported-v","errorCode":null,"errorMessage":"subscriber %v argument type not exported: %v","messagePattern":"subscriber (.+?) argument type not exported: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/grpc/subscriber.go","lineNumber":128,"sourceCode":"\t\tendpoints:  endpoints,\n\t\topts:       options,\n\t}\n}\n\nfunc validateSubscriber(sub server.Subscriber) error {\n\ttyp := reflect.TypeOf(sub.Subscriber())\n\tvar argType reflect.Type\n\n\tif typ.Kind() == reflect.Func {\n\t\tname := \"Func\"\n\t\tswitch typ.NumIn() {\n\t\tcase 2:\n\t\t\targType = typ.In(1)\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"subscriber %v takes wrong number of args: %v required signature %s\", name, typ.NumIn(), subSig)\n\t\t}\n\t\tif !isExportedOrBuiltinType(argType) {\n\t\t\treturn fmt.Errorf(\"subscriber %v argument type not exported: %v\", name, argType)\n\t\t}\n\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)","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/server/grpc/subscriber.go#L110-L146","documentation":"validateSubscriber reflects on the subscriber function's message argument and requires its type to be exported (or a builtin). The gRPC server must construct and decode values of this type from incoming wire messages, and unexported types defined inside another package cannot be instantiated or named by the codec, so registration fails.","triggerScenarios":"Passing a subscriber func whose second parameter is an unexported type, e.g. func(ctx context.Context, e *myPrivateEvent) error where myPrivateEvent (or the package containing it) starts with a lowercase letter and lives outside the registering package.","commonSituations":"Defining message structs in an internal package or as unexported types next to the handler; accidentally using a lowercase type name for what should be a public proto-generated message; moving a handler into a shared package without exporting its payload type.","solutions":["Export the message type (capitalize it, e.g. type Event struct) so it is visible outside its package.","Use the generated protobuf message types (pb.XxxRequest/pb.XxxEvent) as the payload instead of local structs.","If the type is in an internal/ package, move it to an importable exported package.","If it must stay unexported, wrap it: declare an exported wrapper struct containing the payload."],"exampleFix":"// before\nfunc(ctx context.Context, e *eventPayload) error { ... } // unexported\n// after\ntype EventPayload struct{ ... }\nfunc(ctx context.Context, e *EventPayload) error { ... }","handlingStrategy":"validation","validationCode":"func exportedArg(fn interface{}) bool {\n\tt := reflect.TypeOf(fn)\n\tif t == nil || t.Kind() != reflect.Func || t.NumIn() != 2 { return false }\n\ta := t.In(1)\n\tif a.Kind() == reflect.Pointer { a = a.Elem() }\n\treturn a.PkgPath() == \"\" // empty PkgPath => builtin or exported\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use exported (capitalized) message types, ideally generated protobuf types.","Avoid internal/ packages for subscriber payload types.","Wrap payloads in an exported struct if the underlying type must stay hidden.","Check handler registration in a unit test that calls Subscribe during test setup."],"tags":["go","grpc","subscriber","reflection","exported-types"],"backgroundTag":"unexported-handler-argument-type","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}