k3s-io/k3s · error

no server found for %s

Error message

no server found for %s

What it means

serverList.setHealthCheck (exposed via LoadBalancer.SetHealthCheck) looks up an entry by address with sl.getServer(address); if no server in the current list matches, it returns this error. It signals an ordering or programming mistake by the caller driving the embedded load balancer: the address was never added (Update/serverAddresses) or was removed before the call.

Source

Thrown at pkg/agent/loadbalancer/servers.go:195

// getServer returns the first server with the specified address
func (sl *serverList) getServer(address string) *server {
	sl.mutex.Lock()
	defer sl.mutex.Unlock()

	if i := slices.IndexFunc(sl.servers, func(s *server) bool { return s.address == address }); i != -1 {
		return sl.servers[i]
	}
	return nil
}

// setHealthCheck updates the health check function for a server, replacing the
// current function.
func (sl *serverList) setHealthCheck(address string, healthCheck HealthCheckFunc) error {
	if s := sl.getServer(address); s != nil {
		s.healthCheck = healthCheck
		return nil
	}
	return fmt.Errorf("no server found for %s", address)
}

// recordSuccess records a successful check of a server, either via health-check or dial.
// The server's state is adjusted accordingly.
func (sl *serverList) recordSuccess(srv *server, r reason) {
	var new_state state
	switch srv.state {
	case stateFailed, stateUnchecked:
		// dialed or health checked OK once, improve to recovering
		new_state = stateRecovering
	case stateRecovering:
		if r == reasonHealthCheck {
			// was recovering due to successful dial or first health check, can now improve
			if len(srv.connections) > 0 {
				// server accepted connections while recovering, attempt to go straight to active
				new_state = stateActive
			} else {
				// no connections, just make it preferred

View on GitHub (pinned to 6ba341e396)

Solutions

  1. Add/update the address first (lb.Update with the full address list) before installing the health check
  2. Re-fetch the current address list and retry if concurrent updates are possible
  3. Treat as a caller bug: log the target address plus lb.ServerAddresses() to pinpoint the mismatch

Example fix

// before
lb.SetHealthCheck(addr, fn) // addr not in the list yet

// after
lb.Update(append(lb.ServerAddresses(), addr))
lb.SetHealthCheck(addr, fn)
Defensive patterns

Strategy: validation

Validate before calling

found := false
for _, a := range lb.ServerAddresses() {
    if a == addr {
        found = true
        break
    }
}
if !found {
    lb.Update(append(lb.ServerAddresses(), addr))
}
lb.SetHealthCheck(addr, fn)

Prevention

When it happens

Trigger: Calling SetHealthCheck for an address not present in the serverList — after lb.Update replaced the address list, or before the address was ever added; races between config updates and health-check installation.

Common situations: Custom integrations wiring health probes into the agent's LB; reconfiguring server addresses concurrently with startup.

Related errors


AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15). Data as JSON: /api/errors/b26c790381f18f7a. Report an issue: GitHub.