grpc/grpc-go · error
outlier detection: child balancer %q not registered
Error message
outlier detection: child balancer %q not registered
What it means
Returned by outlier detection UpdateClientConnState (internal/xds/balancer/outlierdetection/balancer.go:297) when `balancer.Get(lbCfg.ChildPolicy.Name)` returns nil — i.e. the child LB policy named in the config is not a registered balancer. Outlier detection wraps a child policy and rejects the whole config up front if the child cannot be built.
Source
Thrown at internal/xds/balancer/outlierdetection/balancer.go:298
b.unejectEndpoint(epInfo)
}
// "Reset each endpoint's ejection time multiplier to 0."
epInfo.ejectionTimeMultiplier = 0
}
}
func (b *outlierDetectionBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
lbCfg, ok := s.BalancerConfig.(*LBConfig)
if !ok {
b.logger.Errorf("received config with unexpected type %T: %v", s.BalancerConfig, s.BalancerConfig)
return balancer.ErrBadResolverState
}
// Reject whole config if child policy doesn't exist, don't persist it for
// later.
bb := balancer.Get(lbCfg.ChildPolicy.Name)
if bb == nil {
return fmt.Errorf("outlier detection: child balancer %q not registered", lbCfg.ChildPolicy.Name)
}
// It is safe to read b.cfg here without holding the mutex, as the only
// write to b.cfg happens later in this function. This function is part of
// the balancer.Balancer API, so it is guaranteed to be called in a
// synchronous manner, so it cannot race with this read.
if b.cfg == nil || b.cfg.ChildPolicy.Name != lbCfg.ChildPolicy.Name {
if err := b.child.switchTo(bb); err != nil {
return fmt.Errorf("outlier detection: error switching to child of type %q: %v", lbCfg.ChildPolicy.Name, err)
}
}
b.mu.Lock()
// Inhibit child picker updates until this UpdateClientConnState() call
// completes. If needed, a picker update containing the no-op config bit
// determined from this config and most recent state from the child will be
// sent synchronously upward at the end of this UpdateClientConnState()
// call.View on GitHub (pinned to 03255a9237)
Solutions
- Ensure the child policy name matches a registered balancer (e.g. round_robin, weighted_target)
- Import the package of any custom child balancer so its init() registers it
- Fix the control plane to only send policy names the client supports
Example fix
// before: custom policy never registered, config references "acme_lb" // after import _ "example.com/acme/grpcbal/acmelb" // registers "acme_lb" via balancer.Register in init()
Defensive patterns
Strategy: validation
Validate before calling
// Confirm the child policy is a registered balancer before applying.
if lbCfg.ChildPolicy == nil || balancer.Get(lbCfg.ChildPolicy.Name) == nil {
return fmt.Errorf("child balancer %q is not registered", childPolicyName(lbCfg))
} Type guard
func hasRegisteredChild(cfg *outlierdetection.LBConfig) bool {
return cfg != nil && cfg.ChildPolicy != nil && balancer.Get(cfg.ChildPolicy.Name) != nil
} Try / catch
if err := bal.UpdateClientConnState(state); err != nil {
if strings.Contains(err.Error(), "not registered") {
logger.Errorf("child policy unavailable; keeping previous config: %v", err)
}
return err
} Prevention
- Blanket-import custom balancer packages so init() registers them
- Restrict the control plane to policy names the deployed client supports
- Log child policy names at startup to catch version skew
When it happens
Trigger: UpdateClientConnState is called with an LBConfig whose ChildPolicy.Name is not a registered balancer builder (e.g. "my_custom_lb" when only stock policies are registered, or a typo like "roud_robin").
Common situations: Control plane references an LB policy the binary doesn't support; a custom LB policy wasn't registered via balancer.Register or its init() wasn't imported; version skew where a newer policy name reaches an older client.
Related errors
- OutlierDetectionLoadBalancingConfig.interval = %s; must be >
- OutlierDetectionLoadBalancingConfig.base_ejection_time = %s;
- OutlierDetectionLoadBalancingConfig.max_ejection_time = %s;
- OutlierDetectionLoadBalancingConfig.max_ejection_percent = %
- OutlierDetectionLoadBalancingConfig.SuccessRateEjection.enfo
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/56c0ed1225df77c7.
Report an issue: GitHub.