geektutu/7days-golang · error

rpc: service already defined:

Error message

rpc: service already defined: 

What it means

Register calls LoadOrStore on the server's serviceMap; if a service with the same derived name is already present, it returns this duplicate-registration error instead of overwriting the existing service. This protects against accidentally replacing method tables.

Source

Thrown at gee-rpc/day5-http-debug/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. Ensure Register is called only once per service per Server instance (guard with sync.Once or init)
  2. Use a distinct exported type name for each service so the derived names don't collide
  3. Create a fresh Server for each test case instead of reusing one, or ignore the duplicate error intentionally if re-registration is expected

Example fix

// before
srv.Register(new(Foo))
srv.Register(new(Foo)) // duplicate
// after
var once sync.Once
func registerFoo(s *geeprc.Server) {
    once.Do(func() { s.Register(new(Foo)) })
}
Defensive patterns

Strategy: try-catch

Validate before calling

type Registrable interface{ ServiceName() string }
func alreadyRegistered(server *geeprc.Server, name string) bool {
    seen, _ := registeredNames.Load(name) // track names in a sync.Map of your own
    return seen != nil
}

Try / catch

if err := srv.Register(new(Foo)); err != nil {
    if strings.Contains(err.Error(), "service already defined") {
        log.Printf("Foo already registered; skipping")
    } else {
        return err
    }
}

Prevention

When it happens

Trigger: Calling Register(new(Foo)) twice on the same Server, or registering two receivers whose type names are identical (e.g. two structs both named Foo in different packages).

Common situations: Test suites re-registering fixtures in setup code without a fresh Server, accidentally registering the same receiver on both DefaultServer and a custom Server reused across tests, or name collisions between same-named types.

Related errors


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