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

  1. Set errorUtilizationPenalty to 0 or a positive value (default 1.0).
  2. 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

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


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 Name

View on GitHub (pinned to 0c51461d27)