grpc/grpc-go · error · balancer.ErrBadResolverState
pickfirst: received illegal BalancerConfig (type %T): %v: %w
Error message
pickfirst: received illegal BalancerConfig (type %T): %v: %w
What it means
Returned by pickfirst's UpdateClientConnState() (balancer/pickfirst/pickfirst.go:225). When state.BalancerConfig is non-nil but is not of type pfConfig, the type assertion at line 223 fails and this error is returned, wrapping balancer.ErrBadResolverState. It indicates the resolver or a parent LB policy delivered the wrong Go type as the pick_first config.
Source
Thrown at balancer/pickfirst/pickfirst.go:225
})
}
func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState) error {
b.mu.Lock()
defer b.mu.Unlock()
b.cancelConnectionTimer()
if len(state.ResolverState.Addresses) == 0 && len(state.ResolverState.Endpoints) == 0 {
// Cleanup state pertaining to the previous resolver state.
// Treat an empty address list like an error by calling b.ResolverError.
b.closeSubConnsLocked()
b.addressList.updateAddrs(nil)
b.resolverErrorLocked(errors.New("produced zero addresses"))
return balancer.ErrBadResolverState
}
b.healthCheckingEnabled = state.ResolverState.Attributes.Value(enableHealthListenerKeyType{}) != nil
cfg, ok := state.BalancerConfig.(pfConfig)
if state.BalancerConfig != nil && !ok {
return fmt.Errorf("pickfirst: received illegal BalancerConfig (type %T): %v: %w", state.BalancerConfig, state.BalancerConfig, balancer.ErrBadResolverState)
}
if b.logger.V(2) {
b.logger.Infof("Received new config %s, resolver state %s", pretty.ToJSON(cfg), pretty.ToJSON(state.ResolverState))
}
var newAddrs []resolver.Address
if endpoints := state.ResolverState.Endpoints; len(endpoints) != 0 {
// Perform the optional shuffling described in gRFC A62. The shuffling
// will change the order of endpoints but not touch the order of the
// addresses within each endpoint. - A61
if cfg.ShuffleAddressList {
if envconfig.PickFirstWeightedShuffling {
type weightedEndpoint struct {
endpoint resolver.Endpoint
weight float64
}
View on GitHub (pinned to 0c51461d27)
Solutions
- Ensure the resolver / parent policy that builds the pick_first config produces a pfConfig value (or nil for no config).
- If you wrote a custom balancer wrapping pick_first, pass nil BalancerConfig or a pfConfig.
- Check for grpc-go version mismatches between modules in a vendored/forked build.
- The error wraps ErrBadResolverState, so the channel will request re-resolution — fix the config source.
Example fix
// before: parent passes raw JSON / wrong type to pick_first child
cc.UpdateState(balancer.State{Picker: p})
child.UpdateClientConnState(balancer.ClientConnState{BalancerConfig: someOtherConfig})
// after: pass a pfConfig (or nil)
child.UpdateClientConnState(balancer.ClientConnState{BalancerConfig: pickfirst.PfConfig{}})
// or simply omit config by passing nil Defensive patterns
Strategy: type-guard
Type guard
// Type guard: confirm the config is a pickfirst pfConfig before forwarding.
func isPFConfig(v any) bool {
_, ok := v.(pickfirst.PfConfig)
return ok
}
// Usage:
// if state.BalancerConfig != nil && !isPFConfig(state.BalancerConfig) { ... handle ... } Prevention
- When wrapping pick_first in a parent balancer, always pass nil or a pfConfig as BalancerConfig.
- Do not forward raw json.RawMessage or other policies' config types to a pick_first child.
- Pin grpc-go versions across modules to avoid pfConfig type drift.
When it happens
Trigger: UpdateClientConnState is called with a ClientConnState whose BalancerConfig is a non-nil value that is not a pfConfig (e.g. a json.RawMessage, a *iringhash.LBConfig, or some other policy's config struct). This is typically an internal wiring error in a parent balancer or a custom resolver, not a user-facing JSON typo.
Common situations: A custom resolver or parent LB policy forwards the wrong config type to a pick_first child; a balancer is registered under the pick_first name with a different config type; version skew between grpc-go and a fork/vendored copy that changed pfConfig.
Related errors
- unexpected balancer config with type: %T
- pickfirst: unable to unmarshal LB policy config: %s, error:
- least-request: unable to unmarshal LBConfig: %v
- least-request: lbConfig.choiceCount: %v, must be >= 2
- name resolver error: %v
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/62bd829b1c88698a.
Report an issue: GitHub.