grpc/grpc-go · error

invalid loadBalancingConfig: entry %v does not contain exact

Error message

invalid loadBalancingConfig: entry %v does not contain exactly 1 policy/config pair: %q

What it means

This error occurs in BalancerConfig.UnmarshalJSON when a loadBalancingConfig array entry is a JSON object that does not contain exactly one key (policy name -> config). gRPC's service config spec requires each entry to be a single-key map like [{"round_robin": {}}]. Entries with zero keys ({}) or multiple keys ({"a":{}, "b":{}}) are rejected.

Source

Thrown at internal/serviceconfig/serviceconfig.go:84

//
// ServiceConfig contains a list of loadBalancingConfigs, each with a name and
// config. This method iterates through that list in order, and stops at the
// first policy that is supported.
//   - If the config for the first supported policy is invalid, the whole service
//     config is invalid.
//   - If the list doesn't contain any supported policy, the whole service config
//     is invalid.
func (bc *BalancerConfig) UnmarshalJSON(b []byte) error {
	var ir intermediateBalancerConfig
	err := json.Unmarshal(b, &ir)
	if err != nil {
		return err
	}

	var names []string
	for i, lbcfg := range ir {
		if len(lbcfg) != 1 {
			return fmt.Errorf("invalid loadBalancingConfig: entry %v does not contain exactly 1 policy/config pair: %q", i, lbcfg)
		}

		var (
			name    string
			jsonCfg json.RawMessage
		)
		// Get the key:value pair from the map. We have already made sure that
		// the map contains a single entry.
		for name, jsonCfg = range lbcfg {
		}

		names = append(names, name)
		builder := balancer.Get(name)
		if builder == nil {
			// If the balancer is not registered, move on to the next config.
			// This is not an error.
			continue
		}

View on GitHub (pinned to 03255a9237)

Solutions

  1. Ensure each loadBalancingConfig array element is a single-key JSON object: [{"policy_name": {config}}].
  2. Split multi-policy entries into separate array elements: [{"a": {}}, {"b": {}}] not [{"a": {}, "b": {}}].
  3. Validate the service config JSON structure against the gRPC service_config.proto before passing it to the client.

Example fix

// before (broken): two policies in one entry
"loadBalancingConfig": [{"round_robin": {}, "pick_first": {}}]

// after (valid): one policy per entry, ordered by preference
"loadBalancingConfig": [{"round_robin": {}}, {"pick_first": {}}]
Defensive patterns

Strategy: validation

Validate before calling

// Validate that each loadBalancingConfig entry is a single-key map
func validateLoadBalancingConfig(raw []byte) error {
    var entries []map[string]json.RawMessage
    if err := json.Unmarshal(raw, &entries); err != nil { return err }
    for i, e := range entries {
        if len(e) != 1 {
            return fmt.Errorf("entry %d must have exactly 1 policy, got %d", i, len(e))
        }
    }
    return nil
}

Try / catch

var bc serviceconfig.BalancerConfig
if err := json.Unmarshal(raw, &bc); err != nil {
    if strings.Contains(err.Error(), "does not contain exactly 1") {
        // fix the config source or use default balancer
    }
    return err
}

Prevention

When it happens

Trigger: Parsing a service config whose loadBalancingConfig array contains an entry with 0 or 2+ keys. For example: "loadBalancingConfig": [{"round_robin": {}, "grpclb": {}}] or "loadBalancingConfig": [{}].

Common situations: Malformed service config from a custom name resolver or xDS control plane, hand-crafted JSON that groups multiple policies into one object instead of separate array entries, or tooling that serializes balancer config incorrectly.

Related errors


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