{"record":{"id":"48db52b356cd6e52","repo":"grpc/grpc-go","slug":"pickfirst-unable-to-unmarshal-lb-policy-config","errorCode":null,"errorMessage":"pickfirst: unable to unmarshal LB policy config: %s, error: %v","messagePattern":"pickfirst: unable to unmarshal LB policy config: (.+?), error: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"balancer/pickfirst/pickfirst.go","lineNumber":99,"sourceCode":"\tb := &pickfirstBalancer{\n\t\tcc: cc,\n\n\t\tsubConns:              resolver.NewAddressMapV2[*scData](),\n\t\tstate:                 connectivity.Connecting,\n\t\tcancelConnectionTimer: func() {},\n\t}\n\tb.logger = internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf(logPrefix, b))\n\treturn b\n}\n\nfunc (b pickfirstBuilder) Name() string {\n\treturn Name\n}\n\nfunc (pickfirstBuilder) ParseConfig(js json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {\n\tvar cfg pfConfig\n\tif err := json.Unmarshal(js, &cfg); err != nil {\n\t\treturn nil, fmt.Errorf(\"pickfirst: unable to unmarshal LB policy config: %s, error: %v\", string(js), err)\n\t}\n\treturn cfg, nil\n}\n\n// EnableHealthListener updates the state to configure pickfirst for using a\n// generic health listener.\n//\n// # Experimental\n//\n// Notice: This API is EXPERIMENTAL and may be changed or removed in a later\n// release.\nfunc EnableHealthListener(state resolver.State) resolver.State {\n\tstate.Attributes = state.Attributes.WithValue(enableHealthListenerKeyType{}, true)\n\treturn state\n}\n\ntype pfConfig struct {\n\tserviceconfig.LoadBalancingConfig `json:\"-\"`","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/balancer/pickfirst/pickfirst.go#L81-L117","documentation":"Returned by pick_first's ParseConfig() (balancer/pickfirst/pickfirst.go:99) when json.Unmarshal of the raw config into pfConfig fails. pfConfig has a single optional boolean field 'shuffleAddressList'. The %s shows the raw JSON and %v the unmarshal error. This is the new pick-first leaf balancer (the universal leaf policy). The error fails service config parsing.","triggerScenarios":"The pick_first policy config entry in the service config / xDS resource is invalid JSON, or contains a field of the wrong type (e.g. shuffleAddressList as a string). ParseConfig() is called by the channel during service config parsing.","commonSituations":"Malformed service config JSON string; shuffleAddressList given as \"true\" (string) instead of true (boolean); an xDS control plane sends a malformed pick_first config; trailing comma or unquoted key in a hand-written config.","solutions":["Run the service config JSON through a JSON validator.","Ensure shuffleAddressList is a boolean if present.","Read the %s (raw JSON) and %v (error) to find the exact offending token.","If the config is valid JSON but has unknown fields, note that json.Unmarshal ignores unknown fields unless it is a type mismatch."],"exampleFix":"// before: shuffleAddressList as a string\ngrpc.WithDefaultServiceConfig(`{\"loadBalancingConfig\":[{\"pick_first\":{\"shuffleAddressList\":\"true\"}}]}`)\n\n// after: shuffleAddressList as a boolean\ngrpc.WithDefaultServiceConfig(`{\"loadBalancingConfig\":[{\"pick_first\":{\"shuffleAddressList\":true}}]}`)","handlingStrategy":"validation","validationCode":"// Validate pick_first config JSON before applying.\nfunc validatePickFirstConfig(js string) error {\n    var cfg struct {\n        ShuffleAddressList bool `json:\"shuffleAddressList\"`\n    }\n    return json.Unmarshal([]byte(js), &cfg)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure shuffleAddressList is a JSON boolean, not a string.","Validate service config JSON in CI.","Test config parsing with sample inputs."],"tags":["grpc","balancer","pickfirst","config","json","validation"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}