grpc/grpc-go · error
rls: childPolicy config validation failed: %v
Error message
rls: childPolicy config validation failed: %v
What it means
RLS successfully parsed the child policy's config structurally and handed it (with a dummy target injected) to the child policy's own ConfigParser.ParseConfig. That parser returned an error, meaning the child config violates the child policy's rules. The %v is the child policy's own error message.
Source
Thrown at balancer/rls/config.go:318
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 value
// - marshal the map back to JSON and parse the config using the parser
// retrieved previously
var childConfig map[string]json.RawMessage
if err := json.Unmarshal(rawCfg, &childConfig); err != nil {
return "", nil, fmt.Errorf("rls: json unmarshal failed for child policy config %q: %v", string(rawCfg), err)
}
childConfig[targetFieldName], _ = json.Marshal(dummyChildPolicyTarget)
jsonCfg, err := json.Marshal(childConfig)
if err != nil {
return "", nil, fmt.Errorf("rls: json marshal failed for child policy config {%+v}: %v", childConfig, err)
}
if _, err := parser.ParseConfig(jsonCfg); err != nil {
return "", nil, fmt.Errorf("rls: childPolicy config validation failed: %v", err)
}
return name, childConfig, nil
}
return "", nil, fmt.Errorf("rls: invalid childPolicy config: no supported policies found in %+v", childPolicies)
}
func convertDuration(d *durationpb.Duration) (time.Duration, error) {
if d == nil {
return 0, nil
}
return d.AsDuration(), d.CheckValid()
}
View on GitHub (pinned to 03255a9237)
Solutions
- Read the trailing %v — it is the child policy's specific complaint (e.g. 'wrr: errorUtilizationPenalty must be non-negative').
- Correct the offending field per that child policy's documentation.
- Confirm the field is supported in your gRPC-Go version before reusing it.
Example fix
// before
"childPolicy": [{ "weighted_round_robin": { "errorUtilizationPenalty": -1 } }]
// after
"childPolicy": [{ "weighted_round_robin": { "errorUtilizationPenalty": 1 } }] Defensive patterns
Strategy: validation
Validate before calling
func validateChildConfigViaParser(childPolicy []map[string]json.RawMessage, targetField string) error {
for _, entry := range childPolicy {
for name, raw := range entry {
b := balancer.Get(name)
if b == nil { continue }
p, ok := b.(balancer.ConfigParser)
if !ok { continue }
var m map[string]json.RawMessage
_ = json.Unmarshal(raw, &m)
m[targetField], _ = json.Marshal("dummy")
js, _ := json.Marshal(m)
if _, err := p.ParseConfig(js); err != nil {
return fmt.Errorf("child %s config invalid: %w", name, err)
}
}
}
return nil
} Prevention
- Mirror RLS's own validation (inject dummy target, call child ParseConfig) in your config CI.
- Read the child policy's specific error message — it points at the bad field.
- Confirm each field is supported in your gRPC-Go version.
When it happens
Trigger: Passing a syntactically valid but semantically invalid config to the child — e.g. weighted_round_robin with a negative errorUtilizationPenalty, or round_robin with an unknown field when strict parsing is on. The child policy name was accepted but its contents were rejected.
Common situations: Version skew (using a field only supported in a newer gRPC); typo'd field names that look valid; supplying out-of-range numeric values; copying a config example for a different LB policy.
Related errors
- rls: invalid childPolicy: entry %v does not contain exactly
- rls: childPolicy %q with config %q does not support config p
- 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/eeb95f82b83a758f.
Report an issue: GitHub.