grpc/grpc-go · error
xds_wrr_locality: invalid LBConfig
Error message
xds_wrr_locality: invalid LBConfig: %s, error: %v
What it means
Thrown by the xDS wrr_locality (weighted round-robin locality) custom LB balancer when ParseConfig cannot json.Unmarshal its service config. The config is expected to be a JSON object containing a 'childPolicy' field (per gRFC A52). If the raw JSON handed to the balancer is malformed or the wrong shape, unmarshalling fails here.
Solutions
- Inspect the full error string: it embeds the offending raw JSON (the %s). Compare it against the LBConfig struct {childPolicy} shape.
- Validate the wrr_locality config block in your LDS/CDS response or service config is a JSON object with a childPolicy field before publishing.
- Confirm gRPC client and xDS server support the same xDS custom LB policy version (A52).
- If you are unit-testing this balancer directly, ensure you pass json.RawMessage of a valid JSON object, e.g. {"childPolicy":[{"round_robin":{}}]}.
Example fix
// before (bad raw config passed to balancer)
cfg := json.RawMessage(`not-json`)
parsed, err := bb.ParseConfig(cfg) // returns error 380
// after
cfg := json.RawMessage(`{"childPolicy":[{"round_robin":{}}]}`)
parsed, err := bb.ParseConfig(cfg) Defensive patterns
Strategy: validation
Validate before calling
// Validate an xds_wrr_locality LB config block before handing it to the balancer.
func validateWRRLocalityConfig(raw json.RawMessage) error {
var cfg struct {
ChildPolicy json.RawMessage `json:"childPolicy"`
}
if err := json.Unmarshal(raw, &cfg); err != nil {
return fmt.Errorf("wrr_locality config is not valid JSON: %w", err)
}
if len(cfg.ChildPolicy) == 0 {
return fmt.Errorf("wrr_locality config missing childPolicy")
}
return nil
} Type guard
// Narrow a parsed service config to the wrr_locality LBConfig type.
func isWRRLocalityLBConfig(cfg serviceconfig.LoadBalancingConfig) (*wrrlocality.LBConfig, bool) {
lb, ok := cfg.(*wrrlocality.LBConfig)
return lb, ok && lb != nil && lb.ChildPolicy != nil
} Try / catch
// ParseConfig returns an error, not a panic. Handle it at the resolver/config layer.
parsed, err := bb.ParseConfig(raw)
if err != nil {
return fmt.Errorf("reject service config, bad wrr_locality block: %w", err)
} Prevention
- Always wrap childPolicy in a JSON object before passing to ParseConfig.
- Unit-test balancer ParseConfig with representative configs from your control plane.
- Log the raw JSON on parse failure (the error already includes it) to speed up diagnosis.
When it happens
Trigger: Called when the gRPC core invokes bb.ParseConfig(s json.RawMessage) at balancer.go:92 with a value that fails json.Unmarshal into *LBConfig. This is driven by the xDS resolver translating a service config whose xds_wrr_locality_experimental LB config block is not valid JSON or lacks the expected struct shape.
Common situations: xDS control plane pushes a malformed wrr_locality config block; service config version skew between server and client; manually constructing a service config JSON that omits the object wrapper or passes a non-object value for childPolicy.
Related errors
- xds_wrr_locality: config generated
- failed to push new configuration
- LB policy name found in Children field ( ) is not found in…
- LB policy name found in Priorities field ( ) is not found…
- xds_wrr_locality: error marshalling prepared config
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/82a9469e07d62437.
Report an issue: GitHub.
Appendix: source
Thrown at internal/xds/balancer/wrrlocality/balancer.go:95
wtbCfgParser, ok := builder.(balancer.ConfigParser)
if !ok {
// Shouldn't happen, imported weighted target builder has this method.
return nil
}
wrrL := &wrrLocalityBalancer{
child: wtb,
childParser: wtbCfgParser,
}
wrrL.logger = prefixLogger(wrrL)
wrrL.logger.Infof("Created")
return wrrL
}
func (bb) ParseConfig(s json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
var lbCfg *LBConfig
if err := json.Unmarshal(s, &lbCfg); err != nil {
return nil, fmt.Errorf("xds_wrr_locality: invalid LBConfig: %s, error: %v", string(s), err)
}
if lbCfg == nil || lbCfg.ChildPolicy == nil {
return nil, errors.New("xds_wrr_locality: invalid LBConfig: child policy field must be set")
}
return lbCfg, nil
}
type attributeKey struct{}
// Equal allows the values to be compared by Attributes.Equal.
func (a AddrInfo) Equal(o any) bool {
oa, ok := o.(AddrInfo)
return ok && oa.LocalityWeight == a.LocalityWeight
}
// AddrInfo is the locality weight of the locality an address is a part of.
type AddrInfo struct {
LocalityWeight uint32View on GitHub (pinned to 0c51461d27)