grpc/grpc-go · error
expected a JSON struct with one entry; received entry %v at
Error message
expected a JSON struct with one entry; received entry %v at index %d
What it means
Returned by gracefulswitch.ParseConfig when unmarshalling the loadBalancingConfig JSON array. Each array element must be a JSON object with exactly one key (the LB policy name). If an element has zero keys (empty object) or two or more keys, the parser rejects it with this error. The gracefulswitch balancer uses this format to identify which child policy to activate.
Source
Thrown at internal/balancer/gracefulswitch/config.go:56
func ChildName(l serviceconfig.LoadBalancingConfig) string {
return l.(*lbConfig).childBuilder.Name()
}
// ParseConfig parses a child config list and returns a LB config for the
// gracefulswitch Balancer.
//
// cfg is expected to be a json.RawMessage containing a JSON array of LB policy
// names + configs as the format of the "loadBalancingConfig" field in
// ServiceConfig. It returns a type that should be passed to
// UpdateClientConnState in the BalancerConfig field.
func ParseConfig(cfg json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
var lbCfg []map[string]json.RawMessage
if err := json.Unmarshal(cfg, &lbCfg); err != nil {
return nil, err
}
for i, e := range lbCfg {
if len(e) != 1 {
return nil, fmt.Errorf("expected a JSON struct with one entry; received entry %v at index %d", e, i)
}
var name string
var jsonCfg json.RawMessage
for name, jsonCfg = range e {
}
builder := balancer.Get(name)
if builder == nil {
// Skip unregistered balancer names.
continue
}
parser, ok := builder.(balancer.ConfigParser)
if !ok {
// This is a valid child with no config.
return &lbConfig{childBuilder: builder}, nil
}View on GitHub (pinned to 0c51461d27)
Solutions
- Ensure each element of the loadBalancingConfig JSON array contains exactly one key mapping a policy name to its config object
- Split multi-key entries into separate array elements: [{"a":{}},{"b":{}}] instead of [{"a":{},"b":{}}]
- Validate the JSON structure by unmarshalling into []map[string]json.RawMessage and asserting len(entry)==1 before calling ParseConfig
Example fix
// before (malformed: two keys in one entry)
"loadBalancingConfig": [{"round_robin":{}, "weighted_target":{}}]
// after (correct: one key per entry)
"loadBalancingConfig": [{"round_robin":{}}, {"weighted_target":{}}] Defensive patterns
Strategy: validation
Validate before calling
// Validate loadBalancingConfig JSON before calling gracefulswitch.ParseConfig
func validateLBConfig(raw json.RawMessage) error {
var entries []map[string]json.RawMessage
if err := json.Unmarshal(raw, &entries); err != nil {
return fmt.Errorf("not a JSON array: %w", err)
}
for i, e := range entries {
if len(e) != 1 {
return fmt.Errorf("entry at index %d has %d keys; expected exactly 1", i, len(e))
}
}
return nil
} Prevention
- Always validate the loadBalancingConfig JSON structure before passing it to ParseConfig
- When generating service config programmatically, use a builder pattern that enforces one policy per array element
- Run a JSON schema validator on service config before deployment
When it happens
Trigger: Calling gracefulswitch.ParseConfig() or balancergroup.ParseConfig() (which delegates to it at balancergroup.go:606) with a json.RawMessage whose parsed array contains an entry that is not a single-key map. For example, [{"round_robin":{},"grpclb":{}}] puts two policies in one entry, or [{}] has zero keys.
Common situations: A hand-edited or programmatically-generated service config JSON where multiple LB policies are placed inside a single array element instead of being split across separate elements. This commonly occurs when developers unfamiliar with the gRPC loadBalancingConfig format try to list fallback policies.
Related errors
- least-request: unable to unmarshal LBConfig: %v
- pickfirst: unable to unmarshal LB policy config: %s, error:
- randomsubsetting: json.Unmarshal failed for configuration: %
- error parsing config for policy %q: %v
- no supported policies found in config: %v
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/8e080ff950949fb0.
Report an issue: GitHub.