{"record":{"id":"cd239d58fd2885e5","repo":"micro/go-micro","slug":"rpc-register-type-sname-is-not-exported","errorCode":null,"errorMessage":"rpc Register: type ${sname} is not exported","messagePattern":"rpc Register: type (.+?) is not exported","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/grpc/server.go","lineNumber":152,"sourceCode":"\nfunc (server *rServer) register(rcvr interface{}) error {\n\tserver.mu.Lock()\n\tdefer server.mu.Unlock()\n\tlog := server.logger\n\tif server.serviceMap == nil {\n\t\tserver.serviceMap = make(map[string]*service)\n\t}\n\ts := new(service)\n\ts.typ = reflect.TypeOf(rcvr)\n\ts.rcvr = reflect.ValueOf(rcvr)\n\tsname := reflect.Indirect(s.rcvr).Type().Name()\n\tif sname == \"\" {\n\t\tlogger.Fatalf(\"rpc: no service name for type %v\", s.typ.String())\n\t}\n\tif !isExported(sname) {\n\t\ts := \"rpc Register: type \" + sname + \" is not exported\"\n\t\tlog.Log(logger.ErrorLevel, s)\n\t\treturn errors.New(s)\n\t}\n\tif _, present := server.serviceMap[sname]; present {\n\t\treturn errors.New(\"rpc: service already defined: \" + sname)\n\t}\n\ts.name = sname\n\ts.method = make(map[string]*methodType)\n\n\t// Install the methods\n\tfor m := 0; m < s.typ.NumMethod(); m++ {\n\t\tmethod := s.typ.Method(m)\n\t\tif mt := prepareEndpoint(method, log); mt != nil {\n\t\t\ts.method[method.Name] = mt\n\t\t}\n\t}\n\n\tif len(s.method) == 0 {\n\t\ts := \"rpc Register: type \" + sname + \" has no exported methods of suitable type\"\n\t\tlog.Log(logger.ErrorLevel, s)","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/server/grpc/server.go#L134-L170","documentation":"The gRPC server's register() refuses to register a service whose concrete Go type name is unexported (lowercase first letter), because gRPC method names must be exported to be callable. The error interpolates the offending type name via ${sname}.","triggerScenarios":"Registering a receiver whose type is defined lowercase, e.g. func (g *greeterHandler) ... with type greeterHandler struct, then pb.RegisterGreeterServer / server.register on it.","commonSituations":"Defining handlers inside a function or package with unexported type names; refactoring that renamed an exported handler type to lowercase; generated code wired to a private implementation struct.","solutions":["Rename the handler struct so its type name starts with an uppercase letter (exported)","Wrap the unexported type in an exported type and register that instead","Keep handler types exported even if their methods' logic is internal"],"exampleFix":"// before\ntype greeterHandler struct{}\nserver.register(greeterHandler{}) // rpc Register: type greeterHandler is not exported\n// after\ntype GreeterHandler struct{}\nserver.register(GreeterHandler{})","handlingStrategy":"validation","validationCode":"t := reflect.TypeOf(handler)\nname := t.Name()\nif name == \"\" || !unicode.IsUpper(rune(name[0])) {\n  return fmt.Errorf(\"handler type %s must be exported\", name)\n}","typeGuard":"func isExportedType(v interface{}) bool {\n  n := reflect.TypeOf(v).Name()\n  return n != \"\" && unicode.IsUpper(rune(n[0]))\n}","tryCatchPattern":"if err := srv.register(h); err != nil {\n  if strings.Contains(err.Error(), \"is not exported\") {\n    return fmt.Errorf(\"rename handler type to be exported: %w\", err)\n  }\n  return err\n}","preventionTips":["Name handler structs with uppercase first letters (e.g. GreeterHandler)","Add a startup check that all registered handler types are exported","Review refactors that rename handler types for case changes"],"tags":["grpc","rpc","naming","registration"],"backgroundTag":"rpc-type-not-exported","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}