micro/go-micro · error

rpc.Handle: type ${name} is not exported

Error message

rpc.Handle: type ${name} is not exported

What it means

Handle was called with a handler whose Name() is non-empty but not exported (does not start with an uppercase letter). The router only registers exported names so they can be addressed by external clients; unexported names are rejected at registration time.

Source

Thrown at server/rpc_router.go:465

func (router *router) NewHandler(h interface{}, opts ...HandlerOption) Handler {
	return NewRpcHandler(h, opts...)
}

func (router *router) Handle(h Handler) error {
	router.mu.Lock()
	defer router.mu.Unlock()

	if router.serviceMap == nil {
		router.serviceMap = make(map[string]*service)
	}

	if len(h.Name()) == 0 {
		return errors.New("rpc.Handle: handler has no name")
	}

	if !isExported(h.Name()) {
		return errors.New("rpc.Handle: type " + h.Name() + " is not exported")
	}

	rcvr := h.Handler()
	s := new(service)
	s.typ = reflect.TypeOf(rcvr)
	s.rcvr = reflect.ValueOf(rcvr)

	// check name
	if _, present := router.serviceMap[h.Name()]; present {
		return errors.New("rpc.Handle: service already defined: " + h.Name())
	}

	s.name = h.Name()
	s.method = make(map[string]*methodType)

	// Install the methods
	for m := 0; m < s.typ.NumMethod(); m++ {
		method := s.typ.Method(m)

View on GitHub (pinned to 24529f1404)

Solutions

  1. Change the handler name to start with an uppercase letter (e.g. "Calculator")
  2. If the name comes from config, update it to an exported-style identifier
  3. When deriving names from Go types, use an exported type or map to an explicit exported name

Example fix

// before
router.NewHandler(NewCalcService("calculator"))
// after
router.NewHandler(NewCalcService("Calculator"))
Defensive patterns

Strategy: validation

Validate before calling

name := h.Name()
if name == "" || !unicode.IsUpper(rune(name[0])) {
	return fmt.Errorf("service name %q must be exported (start with uppercase)", name)
}
router.NewHandler(h)

Try / catch

if err := router.NewHandler(h); err != nil {
	if strings.Contains(err.Error(), "is not exported") {
		return fmt.Errorf("fix service name for %T: %w", h, err)
	}
	return err
}

Prevention

When it happens

Trigger: Registering a handler named e.g. "calculator" or "_internal"; generating service names from Go type names of unexported types.

Common situations: Auto-deriving the service name from an unexported Go struct type; lowercase name strings in config files; renaming a service to a lowercase identifier during refactoring.

Related errors


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