grpc/grpc-go · error
rls: invalid childPolicy: entry %v does not contain exactly
Error message
rls: invalid childPolicy: entry %v does not contain exactly 1 policy/config pair: %q
What it means
The childPolicy field in an RLS service config is a list of single-entry maps, each mapping one LB policy name to its config (the prioritized child policy list). parseChildPolicyConfigs requires every list entry to have exactly one policy->config key; an entry with zero or multiple keys is ambiguous and rejected.
Source
Thrown at balancer/rls/config.go:287
cacheSizeBytes = maxCacheSize
}
return &lbConfig{
kbMap: kbMap,
lookupService: lookupService,
lookupServiceTimeout: lookupServiceTimeout,
maxAge: maxAge,
staleAge: staleAge,
cacheSizeBytes: cacheSizeBytes,
defaultTarget: rlsProto.GetDefaultTarget(),
}, nil
}
// parseChildPolicyConfigs iterates through the list of child policies and picks
// the first registered policy and validates its config.
func parseChildPolicyConfigs(childPolicies []map[string]json.RawMessage, targetFieldName string) (string, map[string]json.RawMessage, error) {
for i, config := range childPolicies {
if len(config) != 1 {
return "", nil, fmt.Errorf("rls: invalid childPolicy: entry %v does not contain exactly 1 policy/config pair: %q", i, config)
}
var name string
var rawCfg json.RawMessage
for name, rawCfg = range config {
}
builder := balancer.Get(name)
if builder == nil {
continue
}
parser, ok := builder.(balancer.ConfigParser)
if !ok {
return "", nil, fmt.Errorf("rls: childPolicy %q with config %q does not support config parsing", name, string(rawCfg))
}
// To validate child policy configs we do the following:
// - unmarshal the raw JSON bytes of the child policy config into a map
// - add an entry with key set to `target_field_name` and a dummy valueView on GitHub (pinned to 03255a9237)
Solutions
- Make each array element a single-key object: [{"pick_first":{}}, {"round_robin":{}}] for fallback ordering, not [{"pick_first":{},"round_robin":{}}].
- Remove any empty {} entries from the ChildPolicy array.
- Validate the array shape before deploying: every element must satisfy len(element)==1.
Example fix
// before
"childPolicy": [
{"pick_first": {}, "round_robin": {}}
]
// after
"childPolicy": [
{"pick_first": {}},
{"round_robin": {}}
] Defensive patterns
Strategy: validation
Validate before calling
func validateChildPolicyShape(childPolicy []map[string]json.RawMessage) error {
if len(childPolicy) == 0 {
return fmt.Errorf("childPolicy list is empty")
}
for i, entry := range childPolicy {
if len(entry) != 1 {
return fmt.Errorf("childPolicy[%d] must have exactly 1 policy/config pair, got %d", i, len(entry))
}
}
return nil
} Prevention
- Remember the gRPC childPolicy grammar: a list of single-key maps, one per priority.
- Validate the shape with the helper above before pushing the config.
- Generate the structure from a typed list of {name, config} pairs instead of hand-writing JSON.
When it happens
Trigger: A ChildPolicy JSON array entry that is {} (empty object) or {"pick_first":{...},"round_robin":{...}} (two policies in one entry). The error reports the offending entry index and the map contents.
Common situations: Hand-authoring the child policy list as a flat object instead of a list of single-key maps; YAML->JSON conversion collapsing structure; misunderstanding the gRPC LB policy grammar where each priority is a separate one-element map.
Related errors
- rls: childPolicy %q with config %q does not support config p
- rls: childPolicy config validation failed: %v
- rls: invalid childPolicy config: no supported policies found
- rls: stale_age is set, but max_age is not in route lookup co
- rls: cache_size_bytes must be set to a non-zero value: %+v
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/7b72ae12e0d5e894.
Report an issue: GitHub.