{"record":{"id":"60011aacd3cc7bf4","repo":"grpc/grpc-go","slug":"wrr-errorutilizationpenalty-must-be-non-negative","errorCode":null,"errorMessage":"wrr: errorUtilizationPenalty must be non-negative","messagePattern":"wrr: errorUtilizationPenalty must be non-negative","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"balancer/weightedroundrobin/balancer.go","lineNumber":134,"sourceCode":"\t}\n\treturn b\n}\n\nfunc (bb) ParseConfig(js json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {\n\tlbCfg := &lbConfig{\n\t\t// Default values as documented in A58.\n\t\tOOBReportingPeriod:      iserviceconfig.Duration(10 * time.Second),\n\t\tBlackoutPeriod:          iserviceconfig.Duration(10 * time.Second),\n\t\tWeightExpirationPeriod:  iserviceconfig.Duration(3 * time.Minute),\n\t\tWeightUpdatePeriod:      iserviceconfig.Duration(time.Second),\n\t\tErrorUtilizationPenalty: 1,\n\t}\n\tif err := json.Unmarshal(js, lbCfg); err != nil {\n\t\treturn nil, fmt.Errorf(\"wrr: unable to unmarshal LB policy config: %s, error: %v\", string(js), err)\n\t}\n\n\tif lbCfg.ErrorUtilizationPenalty < 0 {\n\t\treturn nil, fmt.Errorf(\"wrr: errorUtilizationPenalty must be non-negative\")\n\t}\n\n\t// For easier comparisons later, ensure the OOB reporting period is unset\n\t// (0s) when OOB reports are disabled.\n\tif !lbCfg.EnableOOBLoadReport {\n\t\tlbCfg.OOBReportingPeriod = 0\n\t}\n\n\t// Impose lower bound of 100ms on weightUpdatePeriod.\n\tif !internal.AllowAnyWeightUpdatePeriod && lbCfg.WeightUpdatePeriod < iserviceconfig.Duration(100*time.Millisecond) {\n\t\tlbCfg.WeightUpdatePeriod = iserviceconfig.Duration(100 * time.Millisecond)\n\t}\n\n\treturn lbCfg, nil\n}\n\nfunc (bb) Name() string {\n\treturn Name","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/balancer/weightedroundrobin/balancer.go#L116-L152","documentation":"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.","triggerScenarios":"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.","commonSituations":"Typo producing a negative number; misunderstanding the field as a discount rather than a penalty; config templating that injects a sign error.","solutions":["Set errorUtilizationPenalty to 0 or a positive value (default 1.0).","Omit the field to accept the documented default of 1.0."],"exampleFix":"// before\nsc := `{\"loadBalancingConfig\":[{\"weighted_round_robin\":{\"errorUtilizationPenalty\":-1}}]}`\n// after\nsc := `{\"loadBalancingConfig\":[{\"weighted_round_robin\":{\"errorUtilizationPenalty\":1}}]}`","handlingStrategy":"validation","validationCode":"func validateWRRPenalty(penalty float64) error {\n\tif penalty < 0 { return fmt.Errorf(\"errorUtilizationPenalty must be >= 0, got %v\", penalty) }\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["go","grpc","wrr","config","validation","balancer"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}