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 (WRR) load balancer validates its service config inside ParseConfig (balancer.go:133). errorUtilizationPenalty is a float64 that scales how much an endpoint's weight is reduced for its error rate (weight = rps/(utilization + errorRate*penalty)). A negative value is rejected because it would make error-prone endpoints appear MORE attractive, inverting the load-balancing intent. The default is 1.0.

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 03255a9237)

Solutions

  1. Set errorUtilizationPenalty to 0 or a positive number (e.g. 1.0) in the service config JSON, or omit the field to use the default 1.0.
  2. If the config comes from xDS, fix the value at the control plane / management server and re-push.
  3. Validate the LB config JSON with a schema or a dry-run parse before applying it to a live channel.

Example fix

// before
{"loadBalancingConfig":{"weighted_round_robin":{"errorUtilizationPenalty":-0.5}}}
// after
{"loadBalancingConfig":{"weighted_round_robin":{"errorUtilizationPenalty":1.0}}}
Defensive patterns

Strategy: validation

Validate before calling

// Validate before applying service config JSON for weighted_round_robin.
func validateWRRCfg(js []byte) error {
    cfg := struct{ ErrorUtilizationPenalty *float64 `json:"errorUtilizationPenalty"` }{}
    if err := json.Unmarshal(js, &cfg); err != nil { return err }
    if cfg.ErrorUtilizationPenalty != nil && *cfg.ErrorUtilizationPenalty < 0 {
        return fmt.Errorf("errorUtilizationPenalty must be >= 0, got %v", *cfg.ErrorUtilizationPenalty)
    }
    return nil
}

Prevention

When it happens

Trigger: Setting "errorUtilizationPenalty" to a negative number in the JSON config for the weighted_round_robin LB policy — via a service config file, xDS control plane, or the WithDefaultServiceConfig / defaultServiceConfigRawJSON dial option. ParseConfig calls json.Unmarshal then checks lbCfg.ErrorUtilizationPenalty < 0.

Common situations: A hand-written service config JSON with a stray minus sign or typo; an xDS management server pushing an out-of-range value; copy-pasting an OOB reporting template and editing the penalty incorrectly.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/60011aacd3cc7bf4. Report an issue: GitHub.