grpc/grpc-go · error
wrr: errorUtilizationPenalty must be non-negative
Error message
wrr: errorUtilizationPenalty must be non-negative
What it means
The weighted_round_robin policy uses errorUtilizationPenalty to scale endpoint weights by error rate (lbConfig.ErrorUtilizationPenalty). A negative penalty would invert the weighting logic and is meaningless, so ParseConfig rejects it (balancer.go:133-134). The default is 1.0.
Solutions
- Set errorUtilizationPenalty to 0 or a positive value (default 1.0).
- Omit the field to accept the documented default of 1.0.
Example fix
// before
sc := `{"loadBalancingConfig":[{"weighted_round_robin":{"errorUtilizationPenalty":-1}}]}`
// after
sc := `{"loadBalancingConfig":[{"weighted_round_robin":{"errorUtilizationPenalty":1}}]}` Defensive patterns
Strategy: validation
Validate before calling
func validateWRRPenalty(penalty float64) error {
if penalty < 0 { return fmt.Errorf("errorUtilizationPenalty must be >= 0, got %v", penalty) }
return nil
} Prevention
- Keep errorUtilizationPenalty >= 0 (default 1.0); omit it to use the default.
- Validate numeric config values at the source before pushing service configs.
- Unit-test the JSON config through ParseConfig to catch sign errors.
When it happens
Trigger: A service config sets errorUtilizationPenalty to a value less than 0 (e.g., -0.5). After unmarshalling, the check at balancer.go:133 fails and returns this error.
Common situations: Typo producing a negative number; misunderstanding the field as a discount rather than a penalty; config templating that injects a sign error.
Related errors
- wrr: unable to unmarshal LB policy config
- 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/60011aacd3cc7bf4.
Report an issue: GitHub.
Appendix: source
Thrown at balancer/weightedroundrobin/balancer.go:134
}
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, nil
}
func (bb) Name() string {
return NameView on GitHub (pinned to 0c51461d27)