micro/go-micro · error

rpc: service already defined: ${sname}

Error message

rpc: service already defined: ${sname}

What it means

The gRPC server keeps a serviceMap keyed by service type name; register() returns this error when a service with the same name is registered a second time on the same server, since duplicate registrations would overwrite method dispatch. The message includes the duplicate name via ${sname}.

Source

Thrown at server/grpc/server.go:155

	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)
		return errors.New(s)
	}
	server.serviceMap[s.name] = s

View on GitHub (pinned to 24529f1404)

Solutions

  1. Remove the duplicate registration call so each service type is registered once per server
  2. Guard registration with a sync.Once or boolean flag in startup code
  3. If re-registration is intended, create a new server instance instead of reusing the old one
  4. Check startup hooks (options, BeforeStart, init) for double invocation

Example fix

// before
pb.RegisterGreeterHandler(srv.Server(), h)
pb.RegisterGreeterHandler(srv.Server(), h) // rpc: service already defined
// after
var once sync.Once
once.Do(func() {
  pb.RegisterGreeterHandler(srv.Server(), h)
})
Defensive patterns

Strategy: validation

Validate before calling

var registered sync.Map
func registerOnce(srv server.Server, name string, h interface{}) error {
  if _, dup := registered.LoadOrStore(name, true); dup {
    return nil // already registered
  }
  return srv.Register(h)
}

Try / catch

if err := srv.register(h); err != nil {
  if strings.Contains(err.Error(), "already defined") {
    return nil // tolerate idempotent re-registration
  }
  return err
}

Prevention

When it happens

Trigger: Calling Register/RegisterSubscriber (or pb.RegisterXxxServer) twice for the same handler type on one server; accidental double-bootstrap where startup code runs registration twice (e.g. both an init path and a manual call); registering the same struct value and pointer under the same name.

Common situations: Copy-pasted registration lines in main(); plugin/module systems that each register the same service; hot-reload logic constructing a new service but re-registering on the old server instance.

Related errors


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