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, nil

View on GitHub (pinned to 03255a9237)

Solutions

  1. Validate the config JSON with jq and against the A58 spec fields (enableOobLoadReport, oobReportingPeriod, blackoutPeriod, weightExpirationPeriod, weightUpdatePeriod, errorUtilizationPenalty).
  2. Use Go duration strings (e.g. "10s") for all *_period fields and a number for errorUtilizationPenalty.
  3. 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

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


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