grpc/grpc-go · error

least-request: unable to unmarshal LBConfig: %v

Error message

least-request: unable to unmarshal LBConfig: %v

What it means

Raised by the least-request balancer's ParseConfig when the JSON service config for the least_request_experimental policy cannot be unmarshalled into LBConfig. The wrapped %v is the encoding/json error. Because ParseConfig is invoked by the channel when applying service config, this error causes the config to be rejected (the channel falls back to the default policy).

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 03255a9237)

Solutions

  1. Validate the least_request_experimental block is valid JSON (run through `jq`).
  2. Ensure choiceCount is a JSON number (uint32), not a string.
  3. Remove any unrecognized fields or upgrade grpc-go so the struct accepts them.
  4. Test the service config JSON by calling ParseConfig directly in a unit test.

Example fix

// before (choiceCount as string -> unmarshal error):
{ "loadBalancingConfig": { "least_request_experimental": { "choiceCount": "2" } } }

// after:
{ "loadBalancingConfig": { "least_request_experimental": { "choiceCount": 2 } } }
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: unmarshal the least_request_experimental config exactly as
// the balancer does, to surface type errors before the channel applies it.
type lrConfig struct {
    ChoiceCount uint32 `json:"choiceCount,omitempty"`
}
func validateLeastRequestConfig(raw json.RawMessage) error {
    var c lrConfig
    if err := json.Unmarshal(raw, &c); err != nil {
        return fmt.Errorf("least-request: unable to unmarshal LBConfig: %v", err)
    }
    if c.ChoiceCount < 2 {
        return fmt.Errorf("least-request: choiceCount %v must be >= 2", c.ChoiceCount)
    }
    return nil
}

Prevention

When it happens

Trigger: The service config contains a least_request_experimental block whose JSON is malformed or whose fields have wrong types (e.g. choiceCount as a string instead of a number). json.Unmarshal fails inside ParseConfig.

Common situations: Typo in the service config JSON; sending choiceCount as "2" (string) instead of 2; a stray field that breaks the struct decode; an older client receiving a config field from a newer schema that the LBConfig struct rejects on type.

Related errors


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