grpc/grpc-go · error
least-request: lbConfig.choiceCount: %v, must be >= 2
Error message
least-request: lbConfig.choiceCount: %v, must be >= 2
What it means
Raised by least-request ParseConfig after a successful unmarshal if choiceCount < 2. Per gRPC A48, the least-request policy samples between choiceCount SubConns; a value below 2 makes the algorithm meaningless (no choice to make) and is explicitly rejected. Note values > 10 are not rejected but silently clamped to 10.
Source
Thrown at balancer/leastrequest/leastrequest.go:73
// ChoiceCount is the number of random SubConns to sample to find the one
// with the fewest outstanding requests. If unset, defaults to 2. If set to
// < 2, the config will be rejected, and if set to > 10, will become 10.
ChoiceCount uint32 `json:"choiceCount,omitempty"`
}
type bb struct{}
func (bb) ParseConfig(s json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
lbConfig := &LBConfig{
ChoiceCount: 2,
}
if err := json.Unmarshal(s, lbConfig); err != nil {
return nil, fmt.Errorf("least-request: unable to unmarshal LBConfig: %v", err)
}
// "If `choice_count < 2`, the config will be rejected." - A48
if lbConfig.ChoiceCount < 2 { // sweet
return nil, fmt.Errorf("least-request: lbConfig.choiceCount: %v, must be >= 2", lbConfig.ChoiceCount)
}
// "If a LeastRequestLoadBalancingConfig with a choice_count > 10 is
// received, the least_request_experimental policy will set choice_count =
// 10." - A48
if lbConfig.ChoiceCount > 10 {
lbConfig.ChoiceCount = 10
}
return lbConfig, nil
}
func (bb) Name() string {
return Name
}
func (bb) Build(cc balancer.ClientConn, bOpts balancer.BuildOptions) balancer.Balancer {
b := &leastRequestBalancer{
ClientConn: cc,
endpointRPCCounts: resolver.NewEndpointMap[*atomic.Int32](),View on GitHub (pinned to 03255a9237)
Solutions
- Set choiceCount to a value between 2 and 10 (inclusive).
- If you want the default behaviour, omit choiceCount entirely (it defaults to 2).
- Remember values > 10 are clamped to 10, so 2-10 is the effective range.
- Lint the service config for choiceCount in [2,10] before deploy.
Example fix
// before:
{ "loadBalancingConfig": { "least_request_experimental": { "choiceCount": 1 } } } // rejected
// after:
{ "loadBalancingConfig": { "least_request_experimental": { "choiceCount": 2 } } } // or omit entirely Defensive patterns
Strategy: validation
Validate before calling
func validateChoiceCount(raw json.RawMessage) error {
var c struct {
ChoiceCount uint32 `json:"choiceCount,omitempty"`
}
if err := json.Unmarshal(raw, &c); err != nil { return err }
// default is 2 when omitted; if explicitly set, must be 2..10
var presence struct{ ChoiceCount *uint32 `json:"choiceCount"` }
json.Unmarshal(raw, &presence)
if presence.ChoiceCount != nil && (*presence.ChoiceCount < 2 || *presence.ChoiceCount > 10) {
return fmt.Errorf("choiceCount %d out of range [2,10]", *presence.ChoiceCount)
}
return nil
} Prevention
- Keep choiceCount in [2,10] (values >10 are silently clamped).
- Omit choiceCount to accept the default of 2.
- Lint service config for choiceCount bounds in CI.
- Document the A48 clamping behaviour for config authors.
When it happens
Trigger: The service config sets choiceCount to 0 or 1 (or omits it in a way that yields < 2 after explicit assignment). The `lbConfig.ChoiceCount < 2` check fires after unmarshal. (Default is 2, so you must have explicitly set it below 2.)
Common situations: Setting choiceCount to 1 thinking it disables sampling; a config generator defaulting to 0; misreading the schema and setting the old default; copying a value from another balancer's config.
Related errors
- least-request: unable to unmarshal LBConfig: %v
- pickfirst: unable to unmarshal LB policy config: %s, error:
- bad resolver state
- duplicated name
- no priority is provided, all priorities are removed
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/3a86c597811714b4.
Report an issue: GitHub.