grpc/grpc-go · error
child policy not registered
Error message
child policy %q not registered
What it means
In UpdateClientConnState (clusterimpl.go:410-412), cluster_impl resolves its child policy name via balancer.Get; if no builder was registered for that name, the entire config is rejected. The child policy is the actual load-distribution algorithm (round_robin, ring_hash, weighted_round_robin, least_request) that cluster_impl delegates endpoint selection to.
Solutions
- Add a blank import for the missing balancer package, e.g. `import _ "google.golang.org/grpc/balancer/ringhash"`.
- Confirm the ChildPolicy.Name string in the xDS Cluster resource exactly matches a registered balancer's Name().
- For custom balancers, ensure init() calls balancer.Register before any grpc.Dial.
Example fix
// before
import _ "google.golang.org/grpc/balancer/roundrobin"
// xDS Cluster resource requests policy: RING_HASH -> error
// after
import (
_ "google.golang.org/grpc/balancer/roundrobin"
_ "google.golang.org/grpc/balancer/ringhash"
) Defensive patterns
Strategy: validation
Validate before calling
// Before sending config, ensure the child policy is registered.
func validateChildPolicyRegistered(name string) error {
if balancer.Get(name) == nil {
return fmt.Errorf("child policy %q is not registered; add a blank import for its package", name)
}
return nil
} Prevention
- Blank-import every balancer package your xDS deployment may reference (roundrobin, ringhash, weightedroundrobin, leastrequest, etc.).
- Cross-check ChildPolicy.Name against balancer.Get at startup in a sanity test.
- Document the set of supported policy names in your service config.
When it happens
Trigger: newConfig.ChildPolicy.Name references a balancer name (e.g. "ring_hash_experimental", "least_request_experimental") for which no Builder was registered via balancer.Register at process start.
Common situations: xDS control plane configures a policy (RING_HASH, LEAST_REQUEST) that the client binary does not link in; misspelled policy name in the xDS Cluster resource; client built with a stripped gRPC that omits optional balancer plugins.
Related errors
- outlier detection: child balancer
- unexpected balancer config with type: %T
- failed to JSON marshal load balancing policy for child
- failed to parse load balancing policy for child
- failed to push new configuration
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/17d54ce39a9b9b26.
Report an issue: GitHub.
Appendix: source
Thrown at internal/xds/balancer/clusterimpl/clusterimpl.go:412
defer clientConnUpdateHook()
b.mu.Lock()
b.inhibitPickerUpdates = true
b.mu.Unlock()
if b.logger.V(2) {
b.logger.Infof("Received configuration: %s", pretty.ToJSON(s.BalancerConfig))
}
newConfig, ok := s.BalancerConfig.(*LBConfig)
if !ok {
return fmt.Errorf("unexpected balancer config with type: %T", s.BalancerConfig)
}
// Need to check for potential errors at the beginning of this function, so
// that on errors, we reject the whole config, instead of applying part of
// it.
bb := balancer.Get(newConfig.ChildPolicy.Name)
if bb == nil {
return fmt.Errorf("child policy %q not registered", newConfig.ChildPolicy.Name)
}
if b.xdsClient == nil {
c := xdsclient.FromResolverState(s.ResolverState)
if c == nil {
return balancer.ErrBadResolverState
}
b.xdsClient = c
}
xdsConfig := xdsresource.XDSConfigFromResolverState(s.ResolverState)
if xdsConfig == nil {
b.logger.Warningf("Received balancer config with no xDS config")
return balancer.ErrBadResolverState
}
clusterCfg := xdsConfig.Clusters[newConfig.Cluster]
clusterUpdate := clusterCfg.Config.Cluster
if err := b.handleSecurityConfig(clusterUpdate.SecurityCfg); err != nil {View on GitHub (pinned to 0c51461d27)