{"record":{"id":"2802c591b02927e5","repo":"grpc/grpc-go","slug":"wrr-unable-to-unmarshal-lb-policy-config-s-err","errorCode":null,"errorMessage":"wrr: unable to unmarshal LB policy config: %s, error: %v","messagePattern":"wrr: unable to unmarshal LB policy config: (.+?), error: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"balancer/weightedroundrobin/balancer.go","lineNumber":130,"sourceCode":"\tb.child = endpointsharding.NewBalancer(b, bOpts, balancer.Get(pickfirst.Name).Build, endpointsharding.Options{})\n\tb.logger = prefixLogger(b)\n\tif b.logger.V(2) {\n\t\tb.logger.Infof(\"Created\")\n\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","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/balancer/weightedroundrobin/balancer.go#L112-L148","documentation":"The weighted_round_robin LB policy parses its JSON config via ParseConfig (balancer.go:120-131). If json.Unmarshal fails on the raw config bytes (malformed JSON or wrong field types), it returns this wrapped error. The config struct is lbConfig with fields enableOobLoadReport, oobReportingPeriod, blackoutPeriod, weightExpirationPeriod, weightUpdatePeriod, errorUtilizationPenalty.","triggerScenarios":"A service config with a loadBalancingConfig entry for \"weighted_round_robin\" whose value is not valid JSON or whose fields have the wrong type (e.g., a string where a number/duration is expected). json.Unmarshal at balancer.go:129 returns a non-nil error.","commonSituations":"Hand-writing the WRR service config JSON; a typo such as a trailing comma or unquoted duration; passing a config intended for a different LB policy under the wrr name; version skew where a field was renamed.","solutions":["Validate the JSON snippet shown in the error against the lbConfig schema (see config.go field names and JSON tags).","Use the documented field names exactly: enableOobLoadReport, oobReportingPeriod, blackoutPeriod, weightExpirationPeriod, weightUpdatePeriod, errorUtilizationPenalty.","Ensure durations are quoted Go time.Duration strings (e.g., \"10s\", \"3m\") and numbers are unquoted."],"exampleFix":"// before\nsc := `{\"loadBalancingConfig\":[{\"weighted_round_robin\":{\"errorUtilizationPenalty\":\"1\"}}]}` // value should be number\n// after\nsc := `{\"loadBalancingConfig\":[{\"weighted_round_robin\":{\"errorUtilizationPenalty\":1}}]}`","handlingStrategy":"validation","validationCode":"func validateWRRConfigJSON(js string) error {\n\tcfg := &lbConfig{ErrorUtilizationPenalty: 1}\n\tdec := json.NewDecoder(strings.NewReader(js))\n\tdec.DisallowUnknownFields()\n\treturn dec.Decode(cfg)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate the WRR JSON snippet before putting it in a service config.","Match field names to config.go JSON tags exactly; use Go duration strings (\"10s\", \"3m\").","Store canonical WRR config snippets and reuse them rather than retyping."],"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"}