grpc/grpc-go · error

rls: childPolicy %q with config %q does not support config p

Error message

rls: childPolicy %q with config %q does not support config parsing

What it means

RLS delegates actual subchannel selection to a child LB policy, and to validate the child config it needs the policy to implement balancer.ConfigParser. If the resolved builder (registered via balancer.Get(name)) does not implement ConfigParser, RLS cannot parse/validate a config for it, so it errors rather than silently accepting an unvalidated config.

Source

Thrown at balancer/rls/config.go:300

// 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 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)

View on GitHub (pinned to 03255a9237)

Solutions

  1. Use a child policy that supports config parsing (round_robin, pick_first, weighted_round_robin are standard).
  2. If the named policy intentionally takes no config, pass an empty config object and confirm the build registers a ConfigParser for it; otherwise switch policies.
  3. Upgrade gRPC-Go — ConfigParser support for standard policies has been broadened over releases.

Example fix

// before
"childPolicy": [{ "custom_experimental_lb": { "someField": 1 } }]
// after
"childPolicy": [{ "round_robin": {} }]
Defensive patterns

Strategy: validation

Validate before calling

func validateChildPolicyRegistered(childPolicy []map[string]json.RawMessage) error {
    for _, entry := range childPolicy {
        for name := range entry {
            b := balancer.Get(name)
            if b == nil {
                continue // handled by error 127
            }
            if _, ok := b.(balancer.ConfigParser); !ok {
                return fmt.Errorf("policy %q does not implement ConfigParser", name)
            }
        }
    }
    return nil
}

Prevention

When it happens

Trigger: Naming a child policy that exists (is registered) but takes no parseable JSON config, or providing a config object for one. E.g. naming a custom or built-in balancer that has no ParseConfig method while also passing it a non-empty config block.

Common situations: Using an older/preview balancer that lacks ConfigParser; referencing a policy by a wrong/legacy name; supplying a config to a no-config policy like a bare pick_first in some builds.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/71b4fbc2b3992463. Report an issue: GitHub.