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

  1. Inspect the echoed raw config in %s and the json error in %v to pinpoint the bad token.
  2. Ensure shuffleAddressList is a JSON boolean, and remove any other fields not in pfConfig.
  3. Validate the service config JSON with `jq` and against the gRPC service config schema.
  4. 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

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


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