grpc/grpc-go · error
rls: invalid childPolicy: entry
Error message
rls: invalid childPolicy: entry %v does not contain exactly 1 policy/config pair: %q
What it means
Emitted from parseChildPolicyConfigs (config.go:287) when a childPolicy array entry (a map[string]json.RawMessage) does not have exactly one key. Each entry must be a single {policyName: config} pair so the balancer can deterministically pick the policy.
Solutions
- Express each candidate policy as its own array entry: [{"round_robin":{}},{"pick_first":{}}]. The balancer picks the first registered one.
- Remove empty {} entries from the childPolicy array.
- Validate the childPolicy shape programmatically before applying (each element must have len == 1).
Example fix
// before
"childPolicy": [ { "round_robin": {}, "pick_first": {} } ]
// after
"childPolicy": [ { "round_robin": {} }, { "pick_first": {} } ] Defensive patterns
Strategy: validation
Validate before calling
func validateChildPolicyEntries(policies []map[string]json.RawMessage) error {
for i, e := range policies {
if len(e) != 1 {
return fmt.Errorf("childPolicy[%d] must have exactly one policy/config pair, got %d", i, len(e))
}
}
return nil
} Type guard
func childPolicyEntriesAreSingleKey(policies []map[string]json.RawMessage) bool {
for _, e := range policies {
if len(e) != 1 {
return false
}
}
return true
} Prevention
- Remember childPolicy is an array of single-key objects, not one multi-key object.
- List fallback candidates as separate array entries.
- Lint the childPolicy shape in CI.
When it happens
Trigger: A childPolicy entry is an empty object {}, or an object with two or more policy keys like {"round_robin":{}, "pick_first":{}}.
Common situations: Hand-authored config bundling fallback policies in one map entry instead of separate array entries; JSON merge of policy overrides producing multi-key entries; misunderstanding the childPolicy grammar (array of single-key maps, not a single multi-key map).
Related errors
- rls: childPolicyConfigTargetFieldName field is not set in…
- rls: bad control channel service config
- rls: bad RouteLookupConfig proto %+v
- rls: cache_size_bytes must be set to a non-zero value: %+v
- rls: childPolicy config validation failed
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/7b72ae12e0d5e894.
Report an issue: GitHub.
Appendix: 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 0c51461d27)