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

  1. Rename the handler struct so its type name starts with an uppercase letter (exported)
  2. Wrap the unexported type in an exported type and register that instead
  3. 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

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


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/cd239d58fd2885e5. Report an issue: GitHub.