grpc/grpc-go · error

least-request: unable to unmarshal LBConfig

Error message

least-request: unable to unmarshal LBConfig: %v

What it means

Returned by least_request_experimental's ParseConfig() (balancer/leastrequest/leastrequest.go:69) when json.Unmarshal of the raw LB config into LBConfig fails. The %v is the underlying json error. LBConfig expects an optional numeric 'choiceCount' field; any JSON syntax error or type mismatch triggers this. The error propagates up through the service config parser and prevents the channel from applying the policy.

Solutions

  1. Validate the JSON config string with a JSON linter before applying it.
  2. Ensure choiceCount (if present) is a JSON number, not a string.
  3. Check the embedded json error (%v) for the exact field and reason.
  4. If the config comes from xDS, inspect the LDS/CDS resource the control plane sent.

Example fix

// before: choiceCount as a string — invalid JSON unmarshal
grpc.WithDefaultServiceConfig(`{"loadBalancingConfig":[{"least_request_experimental":{"choiceCount":"2"}}]}`)

// after: choiceCount as a number (or omitted to default to 2)
grpc.WithDefaultServiceConfig(`{"loadBalancingConfig":[{"least_request_experimental":{"choiceCount":2}}]}`)
Defensive patterns

Strategy: validation

Validate before calling

// Validate the least_request config JSON before applying it.
func validateLeastRequestConfig(js string) error {
    var cfg struct {
        ChoiceCount uint32 `json:"choiceCount,omitempty"`
    }
    if err := json.Unmarshal([]byte(js), &cfg); err != nil {
        return fmt.Errorf("invalid least_request config JSON: %w", err)
    }
    return nil
}

Prevention

When it happens

Trigger: The service config / xDS LB policy config for least_request_experimental contains malformed JSON or a wrong type for choiceCount (e.g. a string or object). ParseConfig() is invoked by the channel when parsing the loadBalancingConfig entry, and json.Unmarshal returns a non-nil error.

Common situations: A typo in the JSON service config (trailing comma, unquoted key); choiceCount passed as a string "2" instead of number 2; a malformed config pushed by an xDS control plane; hand-written service config string with invalid JSON.

Related errors


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

Appendix: source

Thrown at balancer/leastrequest/leastrequest.go:69

// LBConfig is the balancer config for least_request_experimental balancer.
type LBConfig struct {
	serviceconfig.LoadBalancingConfig `json:"-"`

	// 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
}

View on GitHub (pinned to 0c51461d27)