grpc/grpc-go · error
wrr: unable to unmarshal LB policy config
Error message
wrr: unable to unmarshal LB policy config: %s, error: %v
What it means
The weighted_round_robin LB policy parses its JSON config via ParseConfig (balancer.go:120-131). If json.Unmarshal fails on the raw config bytes (malformed JSON or wrong field types), it returns this wrapped error. The config struct is lbConfig with fields enableOobLoadReport, oobReportingPeriod, blackoutPeriod, weightExpirationPeriod, weightUpdatePeriod, errorUtilizationPenalty.
Solutions
- Validate the JSON snippet shown in the error against the lbConfig schema (see config.go field names and JSON tags).
- Use the documented field names exactly: enableOobLoadReport, oobReportingPeriod, blackoutPeriod, weightExpirationPeriod, weightUpdatePeriod, errorUtilizationPenalty.
- Ensure durations are quoted Go time.Duration strings (e.g., "10s", "3m") and numbers are unquoted.
Example fix
// before
sc := `{"loadBalancingConfig":[{"weighted_round_robin":{"errorUtilizationPenalty":"1"}}]}` // value should be number
// after
sc := `{"loadBalancingConfig":[{"weighted_round_robin":{"errorUtilizationPenalty":1}}]}` Defensive patterns
Strategy: validation
Validate before calling
func validateWRRConfigJSON(js string) error {
cfg := &lbConfig{ErrorUtilizationPenalty: 1}
dec := json.NewDecoder(strings.NewReader(js))
dec.DisallowUnknownFields()
return dec.Decode(cfg)
} Prevention
- Validate the WRR JSON snippet before putting it in a service config.
- Match field names to config.go JSON tags exactly; use Go duration strings ("10s", "3m").
- Store canonical WRR config snippets and reuse them rather than retyping.
When it happens
Trigger: A service config with a loadBalancingConfig entry for "weighted_round_robin" whose value is not valid JSON or whose fields have the wrong type (e.g., a string where a number/duration is expected). json.Unmarshal at balancer.go:129 returns a non-nil error.
Common situations: Hand-writing the WRR service config JSON; a typo such as a trailing comma or unquoted duration; passing a config intended for a different LB policy under the wrr name; version skew where a field was renamed.
Related errors
- wrr: errorUtilizationPenalty must be non-negative
- rls: GrpcKeyBuilder in RouteLookupConfig contains repeated…
- rls: GrpcKeyBuilder in RouteLookupConfig contains a Name…
- rls: GrpcKeyBuilder in RouteLookupConfig contains repeated…
- rls: GrpcKeyBuilder in RouteLookupConfig contains repeated…
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/2802c591b02927e5.
Report an issue: GitHub.
Appendix: 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 0c51461d27)