grpc/grpc-go · error
wrr: unable to unmarshal LB policy config: %s, error: %v
Error message
wrr: unable to unmarshal LB policy config: %s, error: %v
What it means
The weighted_round_robin balancer's ParseConfig json.Unmarshal's the incoming LB config into its lbConfig struct (with A58 defaults pre-filled). If the JSON is malformed or contains fields whose types do not match the struct, the unmarshal fails and this error — including the raw config and the json error — is returned to the resolver/gRPC core, which then rejects the service config.
Source
Thrown at balancer/weightedroundrobin/balancer.go:130
b.child = endpointsharding.NewBalancer(b, bOpts, balancer.Get(pickfirst.Name).Build, endpointsharding.Options{})
b.logger = prefixLogger(b)
if b.logger.V(2) {
b.logger.Infof("Created")
}
return b
}
func (bb) ParseConfig(js json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
lbCfg := &lbConfig{
// Default values as documented in A58.
OOBReportingPeriod: iserviceconfig.Duration(10 * time.Second),
BlackoutPeriod: iserviceconfig.Duration(10 * time.Second),
WeightExpirationPeriod: iserviceconfig.Duration(3 * time.Minute),
WeightUpdatePeriod: iserviceconfig.Duration(time.Second),
ErrorUtilizationPenalty: 1,
}
if err := json.Unmarshal(js, lbCfg); err != nil {
return nil, fmt.Errorf("wrr: unable to unmarshal LB policy config: %s, error: %v", string(js), err)
}
if lbCfg.ErrorUtilizationPenalty < 0 {
return nil, fmt.Errorf("wrr: errorUtilizationPenalty must be non-negative")
}
// For easier comparisons later, ensure the OOB reporting period is unset
// (0s) when OOB reports are disabled.
if !lbCfg.EnableOOBLoadReport {
lbCfg.OOBReportingPeriod = 0
}
// Impose lower bound of 100ms on weightUpdatePeriod.
if !internal.AllowAnyWeightUpdatePeriod && lbCfg.WeightUpdatePeriod < iserviceconfig.Duration(100*time.Millisecond) {
lbCfg.WeightUpdatePeriod = iserviceconfig.Duration(100 * time.Millisecond)
}
return lbCfg, nilView on GitHub (pinned to 03255a9237)
Solutions
- Validate the config JSON with jq and against the A58 spec fields (enableOobLoadReport, oobReportingPeriod, blackoutPeriod, weightExpirationPeriod, weightUpdatePeriod, errorUtilizationPenalty).
- Use Go duration strings (e.g. "10s") for all *_period fields and a number for errorUtilizationPenalty.
- Generate the config with encoding/json rather than templating strings.
Example fix
// before
[{ "weighted_round_robin": { "blackoutPeriod": "abc", "errorUtilizationPenalty": "1" } }]
// after
[{ "weighted_round_robin": { "blackoutPeriod": "10s", "errorUtilizationPenalty": 1 } }] Defensive patterns
Strategy: validation
Validate before calling
func validateWRRConfig(js json.RawMessage) error {
cfg := &lbConfig{} // weightedroundrobin.lbConfig with A58 defaults
if err := json.Unmarshal(js, cfg); err != nil {
return fmt.Errorf("invalid wrr config JSON: %w", err)
}
if cfg.ErrorUtilizationPenalty < 0 {
return fmt.Errorf("errorUtilizationPenalty must be non-negative")
}
return nil
} Prevention
- Validate the WRR config with jq before deploying.
- Use Go duration strings for all *_period fields and a number for errorUtilizationPenalty.
- Generate configs programmatically with encoding/json.
When it happens
Trigger: A weighted_round_robin config that is not valid JSON or has wrong-typed fields — e.g. "blackoutPeriod": "abc" (string instead of duration), a trailing comma, or an unexpected top-level type.
Common situations: Hand-editing the WRR service config; mixing up field names/types with another LB policy; tooling that emits non-strict JSON; version skew where a field was renamed.
Related errors
- rls: stale_age is set, but max_age is not in route lookup co
- rls: bad control channel service config %q: %v
- rls: cache_size_bytes must be set to a non-zero value: %+v
- rls: invalid childPolicy: entry %v does not contain exactly
- rls: childPolicy %q with config %q does not support config p
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/2802c591b02927e5.
Report an issue: GitHub.