grpc/grpc-go · error
pickfirst: unable to unmarshal LB policy config: %s, error:
Error message
pickfirst: unable to unmarshal LB policy config: %s, error: %v
What it means
Raised by pickfirst's ParseConfig when the JSON service config for the pick_first policy cannot be unmarshalled into pfConfig. Unlike least-request (error 96), this message ALSO echoes the raw offending config string (%s) alongside the json error (%v), making it easy to see exactly what bytes failed. The channel rejects the config and falls back.
Source
Thrown at balancer/pickfirst/pickfirst.go:125
target: bo.Target.String(),
metricsRecorder: cc.MetricsRecorder(),
subConns: resolver.NewAddressMapV2[*scData](),
state: connectivity.Connecting,
cancelConnectionTimer: func() {},
}
b.logger = internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf(logPrefix, b))
return b
}
func (b pickfirstBuilder) Name() string {
return Name
}
func (pickfirstBuilder) ParseConfig(js json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
var cfg pfConfig
if err := json.Unmarshal(js, &cfg); err != nil {
return nil, fmt.Errorf("pickfirst: unable to unmarshal LB policy config: %s, error: %v", string(js), err)
}
return cfg, nil
}
// EnableHealthListener updates the state to configure pickfirst for using a
// generic health listener.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a later
// release.
func EnableHealthListener(state resolver.State) resolver.State {
state.Attributes = state.Attributes.WithValue(enableHealthListenerKeyType{}, true)
return state
}
type pfConfig struct {
serviceconfig.LoadBalancingConfig `json:"-"`View on GitHub (pinned to 03255a9237)
Solutions
- Inspect the echoed raw config in %s and the json error in %v to pinpoint the bad token.
- Ensure shuffleAddressList is a JSON boolean, and remove any other fields not in pfConfig.
- Validate the service config JSON with `jq` and against the gRPC service config schema.
- Upgrade grpc-go if the config uses a field added in a newer version.
Example fix
// before (shuffleAddressList as string):
{ "loadBalancingConfig": { "pick_first": { "shuffleAddressList": "true" } } }
// after:
{ "loadBalancingConfig": { "pick_first": { "shuffleAddressList": true } } } Defensive patterns
Strategy: validation
Validate before calling
type pfCfg struct {
ShuffleAddressList bool `json:"shuffleAddressList"`
}
func validatePickFirstConfig(raw json.RawMessage) error {
var c pfCfg
dec := json.NewDecoder(bytes.NewReader(raw))
dec.DisallowUnknownFields()
if err := dec.Decode(&c); err != nil {
return fmt.Errorf("pickfirst: unable to unmarshal LB policy config: %s, error: %v", string(raw), err)
}
return nil
} Prevention
- Use DisallowUnknownFields to catch stray pick_first config fields.
- Ensure shuffleAddressList is a JSON boolean.
- Validate the full service config JSON with `jq`.
- Upgrade grpc-go to match the config schema version.
When it happens
Trigger: The service config contains a pick_first block whose JSON is malformed or whose fields have wrong types. pfConfig currently only accepts the boolean shuffleAddressList; any other shape fails. json.Unmarshal fails and the error includes the raw input.
Common situations: Sending shuffleAddressList as a string "true" instead of boolean true; adding an unsupported field to pick_first's config; a malformed JSON payload from a config-discovery service; version skew where a newer field is sent to an older client.
Related errors
- least-request: unable to unmarshal LBConfig: %v
- least-request: lbConfig.choiceCount: %v, must be >= 2
- xds: unable to unmarshal lbconfig: %s, error: %v
- xds: unable to unmarshal LBconfig: %s, error: %v
- bad resolver state
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/48db52b356cd6e52.
Report an issue: GitHub.