grpc/grpc-go · error

pickfirst: unable to unmarshal LB policy config

Error message

pickfirst: unable to unmarshal LB policy config: %s, error: %v

What it means

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.

Solutions

  1. Run the service config JSON through a JSON validator.
  2. Ensure shuffleAddressList is a boolean if present.
  3. Read the %s (raw JSON) and %v (error) to find the exact offending token.
  4. If the config is valid JSON but has unknown fields, note that json.Unmarshal ignores unknown fields unless it is a type mismatch.

Example fix

// before: shuffleAddressList as a string
grpc.WithDefaultServiceConfig(`{"loadBalancingConfig":[{"pick_first":{"shuffleAddressList":"true"}}]}`)

// after: shuffleAddressList as a boolean
grpc.WithDefaultServiceConfig(`{"loadBalancingConfig":[{"pick_first":{"shuffleAddressList":true}}]}`)
Defensive patterns

Strategy: validation

Validate before calling

// Validate pick_first config JSON before applying.
func validatePickFirstConfig(js string) error {
    var cfg struct {
        ShuffleAddressList bool `json:"shuffleAddressList"`
    }
    return json.Unmarshal([]byte(js), &cfg)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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

Appendix: source

Thrown at balancer/pickfirst/pickfirst.go:99

	b := &pickfirstBalancer{
		cc: cc,

		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 0c51461d27)