grpc/grpc-go · error
invalid loadBalancingConfig: entry
Error message
invalid loadBalancingConfig: entry %v does not contain exactly 1 policy/config pair: %q
What it means
Fires in BalancerConfig.UnmarshalJSON (serviceconfig.go:84) when an element of the loadBalancingConfig JSON array is not a single-key map mapping one policy name to its config object. gRPC's service-config schema requires each loadBalancingConfig entry to contain exactly one policy-name -> config pair, so the parser can walk them in order and pick the first supported one.
Solutions
- Reformat the offending entry so each array element is an object with exactly one key: [{"policyA": {}}, {"policyB": {}}].
- Use the library's BalancerConfig.MarshalJSON to generate valid output from a Go struct instead of hand-writing JSON.
- Validate the service config against the official gRPC service_config.proto JSON schema before applying.
Example fix
// before
// "loadBalancingConfig": [
// { "round_robin": {}, "pick_first": {} } // two keys in one entry
// ]
// after
// "loadBalancingConfig": [
// { "round_robin": {} },
// { "pick_first": {} }
// ] Defensive patterns
Strategy: validation
Validate before calling
// Validate that each loadBalancingConfig entry is a single-key map.
func validateLBConfig(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 has %d keys; want exactly 1", i, len(e))
}
}
return nil
} Try / catch
var bc svcconfig.BalancerConfig
if err := json.Unmarshal(lbJSON, &bc); err != nil {
// err contains "does not contain exactly 1 policy/config pair"
return err
} Prevention
- Emit loadBalancingConfig via BalancerConfig.MarshalJSON from Go structs.
- Validate against the service_config.proto JSON schema before applying.
- Remember each array element must be an object with exactly one policy-name key.
When it happens
Trigger: A loadBalancingConfig array entry whose JSON object has zero keys (e.g. [{}]) or two-or-more keys (e.g. [{"round_robin": {}, "grpclb": {}}]). The check len(lbcfg) != 1 catches both. Also triggered by malformed JSON object syntax that json.Unmarshal still parses into a multi-key map.
Common situations: Hand-editing service config and accidentally merging two policies into one object; a config builder that emits all policies into a single map instead of separate array entries; misunderstanding the schema (the array-of-single-key-maps shape is non-obvious).
Related errors
- error parsing loadBalancingConfig for policy
- invalid loadBalancingConfig: no supported policies found in
- malformed duration : contains no numbers
- malformed duration
- duplicated name
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/52acfcbc4d4f0a3d.
Report an issue: GitHub.
Appendix: 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 0c51461d27)