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
- Check the resolver output: log ResolverState.Addresses and ResolverState.Endpoints — an empty set means upstream resolution is broken (DNS, xDS, custom resolver).
- Validate the service config: if the policy has a ParseConfig, ensure the JSON/YAML matches its schema; fix the config in the control plane.
- If you author a balancer, return ErrBadResolverState ONLY for genuinely unrecoverable resolver data so gRPC will back off and re-resolve.
- 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
- Return ErrBadResolverState ONLY for unrecoverable resolver data so backoff kicks in.
- Validate service config JSON in ParseConfig before applying.
- Log resolver.State contents when re-resolution loops persist.
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
- no children to pick from
- no SubConn is available
- all SubConns are in TransientFailure
- weighted-target: no targets to pick from
- last resolver error: %v
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/ef1772e20a9c89b1.
Report an issue: GitHub.