geektutu/7days-golang · error

rpc: service already defined:

Error message

rpc: service already defined: 

What it means

Server.Register stores the receiver by its derived name in serviceMap using LoadOrStore; if a service with that name is already registered it returns this error instead of overwriting. Duplicate registration is refused to keep one canonical implementation per service name.

Source

Thrown at gee-rpc/day7-registry/server.go:223

		}
		go server.ServeConn(conn)
	}
}

// Accept accepts connections on the listener and serves requests
// for each incoming connection.
func Accept(lis net.Listener) { DefaultServer.Accept(lis) }

// Register publishes in the server the set of methods of the
// receiver value that satisfy the following conditions:
//	- exported method of exported type
//	- two arguments, both of exported type
//	- the second argument is a pointer
//	- one return value, of type error
func (server *Server) Register(rcvr interface{}) error {
	s := newService(rcvr)
	if _, dup := server.serviceMap.LoadOrStore(s.name, s); dup {
		return errors.New("rpc: service already defined: " + s.name)
	}
	return nil
}

// Register publishes the receiver's methods in the DefaultServer.
func Register(rcvr interface{}) error { return DefaultServer.Register(rcvr) }

const (
	connected        = "200 Connected to Gee RPC"
	defaultRPCPath   = "/_geeprc_"
	defaultDebugPath = "/debug/geerpc"
)

// ServeHTTP implements an http.Handler that answers RPC requests.
func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if req.Method != "CONNECT" {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusMethodNotAllowed)

View on GitHub (pinned to cf36443821)

Solutions

  1. Call Register only once per service per Server instance (e.g. at startup/init)
  2. Guard registration with sync.Once or check whether the service is already registered
  3. Register a single instance and reuse it instead of re-registering per connection
  4. If you genuinely need a new implementation, create a new Server or use a distinct type name

Example fix

// before
func onConn(s *geerpc.Server) { s.Register(new(Foo)) } // called per connection
// after
var once sync.Once
func setup(s *geerpc.Server) { once.Do(func() { s.Register(new(Foo)) }) }
Defensive patterns

Strategy: try-catch

Validate before calling

// register once per process
var registerOnce sync.Once
func ensureRegistered(s *geerpc.Server) {
    registerOnce.Do(func() {
        if err := s.Register(new(Foo)); err != nil {
            if strings.Contains(err.Error(), "already defined") { return } // idempotent
            log.Fatal(err)
        }
    })
}

Try / catch

if err := srv.Register(new(Foo)); err != nil {
    if strings.Contains(err.Error(), "already defined") {
        return nil // treat as idempotent success
    }
    return err
}

Prevention

When it happens

Trigger: Calling Register twice with the same receiver type (or two types whose derived name collides, e.g. value and pointer of the same type) on the same Server, or registering in code that runs on each reconnect/restart within the same process.

Common situations: Registration placed in a hot path or init function invoked multiple times; registering both Foo and *Foo; tests constructing one server but calling Register in setup run repeatedly.

Related errors


AI-assisted analysis of geektutu/7days-golang@cf36443821 (2026-09-03). Data as JSON: /api/errors/633ee2a6657c05f8. Report an issue: GitHub.