micro/go-micro · error
rpc Register: type ${sname} is not exported
Error message
rpc Register: type ${sname} is not exported What it means
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}.
Source
Thrown at server/grpc/server.go:152
func (server *rServer) register(rcvr interface{}) error {
server.mu.Lock()
defer server.mu.Unlock()
log := server.logger
if server.serviceMap == nil {
server.serviceMap = make(map[string]*service)
}
s := new(service)
s.typ = reflect.TypeOf(rcvr)
s.rcvr = reflect.ValueOf(rcvr)
sname := reflect.Indirect(s.rcvr).Type().Name()
if sname == "" {
logger.Fatalf("rpc: no service name for type %v", s.typ.String())
}
if !isExported(sname) {
s := "rpc Register: type " + sname + " is not exported"
log.Log(logger.ErrorLevel, s)
return errors.New(s)
}
if _, present := server.serviceMap[sname]; present {
return errors.New("rpc: service already defined: " + sname)
}
s.name = sname
s.method = make(map[string]*methodType)
// Install the methods
for m := 0; m < s.typ.NumMethod(); m++ {
method := s.typ.Method(m)
if mt := prepareEndpoint(method, log); mt != nil {
s.method[method.Name] = mt
}
}
if len(s.method) == 0 {
s := "rpc Register: type " + sname + " has no exported methods of suitable type"
log.Log(logger.ErrorLevel, s)View on GitHub (pinned to 24529f1404)
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
Example fix
// before
type greeterHandler struct{}
server.register(greeterHandler{}) // rpc Register: type greeterHandler is not exported
// after
type GreeterHandler struct{}
server.register(GreeterHandler{}) Defensive patterns
Strategy: validation
Validate before calling
t := reflect.TypeOf(handler)
name := t.Name()
if name == "" || !unicode.IsUpper(rune(name[0])) {
return fmt.Errorf("handler type %s must be exported", name)
} Type guard
func isExportedType(v interface{}) bool {
n := reflect.TypeOf(v).Name()
return n != "" && unicode.IsUpper(rune(n[0]))
} Try / catch
if err := srv.register(h); err != nil {
if strings.Contains(err.Error(), "is not exported") {
return fmt.Errorf("rename handler type to be exported: %w", err)
}
return err
} Prevention
- 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
When it happens
Trigger: Registering a receiver whose type is defined lowercase, e.g. func (g *greeterHandler) ... with type greeterHandler struct, then pb.RegisterGreeterServer / server.register on it.
Common situations: 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.
Related errors
- rpc.Handle: type ${name} is not exported
- go.micro.client
- rpc: service already defined: ${sname}
- rpc.Handle: handler has no name
- go.micro.client
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/cd239d58fd2885e5.
Report an issue: GitHub.