geektutu/7days-golang · error

rpc discovery: no available servers

Error message

rpc discovery: no available servers

What it means

Guard in MultiServersDiscovery.Get: the discovery instance's server list is empty (n == 0), so no peer can be selected under any SelectMode. It fires when Update was never called or the known set of servers shrank to nothing.

Source

Thrown at gee-rpc/day7-registry/xclient/discovery.go:55

func (d *MultiServersDiscovery) Refresh() error {
	return nil
}

// Update the servers of discovery dynamically if needed
func (d *MultiServersDiscovery) Update(servers []string) error {
	d.mu.Lock()
	defer d.mu.Unlock()
	d.servers = servers
	return nil
}

// Get a server according to mode
func (d *MultiServersDiscovery) Get(mode SelectMode) (string, error) {
	d.mu.Lock()
	defer d.mu.Unlock()
	n := len(d.servers)
	if n == 0 {
		return "", errors.New("rpc discovery: no available servers")
	}
	switch mode {
	case RandomSelect:
		return d.servers[d.r.Intn(n)], nil
	case RoundRobinSelect:
		s := d.servers[d.index%n] // servers could be updated, so mode n to ensure safety
		d.index = (d.index + 1) % n
		return s, nil
	default:
		return "", errors.New("rpc discovery: not supported select mode")
	}
}

// returns all servers in discovery
func (d *MultiServersDiscovery) GetAll() ([]string, error) {
	d.mu.RLock()
	defer d.mu.RUnlock()
	// return a copy of d.servers

View on GitHub (pinned to cf36443821)

Solutions

  1. Register at least one server instance under the expected prefix in the registry before creating clients
  2. Verify the registry address and service prefix match between server registration and client discovery
  3. Wait for/watch the initial discovery update (registryWatcher) before issuing calls; add a readiness check or brief retry
  4. Handle the error in XClient call paths by refreshing discovery (call d.Update/refreshAndReportToCache) or surfacing 'no backends' to the user

Example fix

// before
d := xclient.NewGeeDiscovery(registry, "@geerpc/"+service) // no servers registered yet
client := xclient.NewXClient(d, xclient.RandomSelect, ...)
// after
// ensure servers register first, then poll for availability
for i := 0; i < 10; i++ {
    if _, err := d.GetAll(); err == nil && len(servers) > 0 { break }
    time.Sleep(500 * time.Millisecond)
}
Defensive patterns

Strategy: retry

Validate before calling

func hasServers(d *xclient.MultiServersDiscovery) bool {
    servers, err := d.GetAll()
    return err == nil && len(servers) > 0
}
// wait for the registry to populate before creating/calling the client

Try / catch

for attempt := 0; attempt < 5; attempt++ {
    err := xc.Call(ctx, "Foo.Sum", args, &reply)
    if err == nil { break }
    if strings.Contains(err.Error(), "no available servers") {
        time.Sleep(500 * time.Millisecond) // registry may not be populated yet
        continue
    }
    return err
}

Prevention

When it happens

Trigger: Creating GeeDiscovery/MultiServersDiscovery with an empty servers slice, constructing a Geo-based client whose initial registry push has not arrived yet, or all servers being removed via Update and then calling Get.

Common situations: Registry (etcd) has no registered servers for the service; service prefix wrong so no instances are fetched; calling a client before the registry watcher populates the list; every backend deregistered after crashes.

Related errors


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