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] = sView on GitHub (pinned to 24529f1404)
Solutions
- Remove the duplicate registration call so each service type is registered once per server
- Guard registration with a sync.Once or boolean flag in startup code
- If re-registration is intended, create a new server instance instead of reusing the old one
- 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
- Register each service exactly once, guarded by sync.Once
- Audit init()/BeforeStart hooks for duplicate registration paths
- Use a fresh server instance when re-registration is required
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
- go.micro.client
- rpc Register: type ${sname} is not exported
- agent: checkpointed run is terminal with status
- cannot init while connected
- not connected
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/67a9d8cfd3335db0.
Report an issue: GitHub.