grpc/grpc-go · error

bad resolver state

Error message

bad resolver state

What it means

balancer.ErrBadResolverState is the sentinel a Balancer returns from UpdateClientConnState to flag that the resolver produced unusable data. Per balancer/balancer.go:345-350, when this is returned the ClientConn starts calling ResolveNow on the active resolver with exponential backoff until a subsequent UpdateClientConnState returns nil. Other returned errors are currently ignored, so this sentinel is the ONLY way to request a re-resolution.

Source

Thrown at balancer/balancer.go:394

type ExitIdler interface {
	// ExitIdle instructs the LB policy to reconnect to backends / exit the
	// IDLE state, if appropriate and possible.  Note that SubConns that enter
	// the IDLE state will not reconnect until SubConn.Connect is called.
	ExitIdle()
}

// ClientConnState describes the state of a ClientConn relevant to the
// balancer.
type ClientConnState struct {
	ResolverState resolver.State
	// The parsed load balancing configuration returned by the builder's
	// ParseConfig method, if implemented.
	BalancerConfig serviceconfig.LoadBalancingConfig
}

// ErrBadResolverState may be returned by UpdateClientConnState to indicate a
// problem with the provided name resolver data.
var ErrBadResolverState = errors.New("bad resolver state")

View on GitHub (pinned to 03255a9237)

Solutions

  1. Check the resolver output: log ResolverState.Addresses and ResolverState.Endpoints — an empty set means upstream resolution is broken (DNS, xDS, custom resolver).
  2. Validate the service config: if the policy has a ParseConfig, ensure the JSON/YAML matches its schema; fix the config in the control plane.
  3. If you author a balancer, return ErrBadResolverState ONLY for genuinely unrecoverable resolver data so gRPC will back off and re-resolve.
  4. Verify the dial target scheme/authority is correct (e.g. dns:///<service>, xds:///<resource>).

Example fix

// before — balancer silently fails, no re-resolution
func (b *lb) UpdateClientConnState(s balancer.ClientConnState) error {
    return fmt.Errorf("no addresses")
}

// after — request backoff re-resolution
func (b *lb) UpdateClientConnState(s balancer.ClientConnState) error {
    if len(s.ResolverState.Addresses) == 0 && s.ResolverState.Endpoints == nil {
        return balancer.ErrBadResolverState
    }
    ...
}
Defensive patterns

Strategy: retry

Validate before calling

// Validate resolver state before your balancer acts on it
func (b *lb) UpdateClientConnState(s balancer.ClientConnState) error {
    if len(s.ResolverState.Addresses) == 0 && len(s.ResolverState.Endpoints) == 0 {
        return balancer.ErrBadResolverState // triggers backoff re-resolution
    }
    return b.apply(s)
}

Try / catch

if errors.Is(err, balancer.ErrBadResolverState) {
    // gRPC is already backing off and re-resolving; surface as Unavailable to caller
}

Prevention

When it happens

Trigger: A Balancer.UpdateClientConnState returns ErrBadResolverState — most often because ClientConnState.ResolverState has no addresses, the service config failed to parse in the balancer's ParseConfig, or the addresses/endpoints are malformed for that policy (e.g. endpointsharding/weightedtarget receiving zero endpoints).

Common situations: A service config JSON typo that makes ParseConfig fail; resolver returning an empty address list (DNS returns nothing, xDS has no hosts); custom balancer that treats missing required metadata as fatal; bootstrap/misconfigured authority yielding no endpoints.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/ef1772e20a9c89b1. Report an issue: GitHub.