grpc/grpc-go · error

failed to parse load balancing policy for child

Error message

failed to parse load balancing policy for child %q: %v

What it means

After successfully marshaling a child policy config to JSON during a type change, cluster_manager hands the bytes to balancergroup.ParseConfig (clustermanager.go:127-132). ParseConfig wraps the JSON in gracefulswitch format; if it rejects the bytes (wrong top-level shape, unknown policy, missing fields), the error is wrapped and an error picker is set for that child only.

Solutions

  1. Compare the marshaled JSON to what balancergroup.ParseConfig expects (a one-element array of {policyName: {...}}).
  2. Update the child balancer package to a version whose MarshalJSON output is gracefulswitch-compatible.
  3. Use channelz logs to inspect the exact bytes that failed parsing.
Defensive patterns

Strategy: try-catch

Try / catch

// Catch the parse failure and treat the named child as broken.
if err := cm.UpdateClientConnState(state); err != nil {
    if strings.Contains(err.Error(), "failed to parse load balancing policy for child") {
        // isolate the failing child; other children keep serving
    }
}

Prevention

When it happens

Trigger: childCfg.ChildPolicy.MarshalJSON produces JSON that balancergroup.ParseConfig rejects (e.g. not a single-element [{name: config}] array, or a policy name balancergroup cannot resolve).

Common situations: A child policy whose JSON output does not match what gracefulswitch expects; control plane sends a config that round-trips incorrectly through the child policy's parser; version drift between gRPC core and the child balancer package.

Understand the failure class

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/23f76a9165f9786d. Report an issue: GitHub.

Appendix: source

Thrown at internal/xds/balancer/clustermanager/clustermanager.go:129

			// child fail, while RPCs to other children with good configs
			// continue to succeed.
			newPolicyName, oldPolicyName := childCfg.ChildPolicy.Name, b.children[childName].ChildPolicy.Name
			if newPolicyName != oldPolicyName {
				var err error
				var cfgJSON []byte
				cfgJSON, err = childCfg.ChildPolicy.MarshalJSON()
				if err != nil {
					retErr = fmt.Errorf("failed to JSON marshal load balancing policy for child %q: %v", childName, err)
					b.setErrorPickerForChild(childName, retErr)
					continue
				}
				// This overwrites lbCfg to be in the format expected by the
				// gracefulswitch balancer. So, when this config is pushed to
				// the child (below), it will result in a graceful switch to the
				// new child policy.
				lbCfg, err = balancergroup.ParseConfig(cfgJSON)
				if err != nil {
					retErr = fmt.Errorf("failed to parse load balancing policy for child %q: %v", childName, err)
					b.setErrorPickerForChild(childName, retErr)
					continue
				}
			}
		}

		if err := b.bg.UpdateClientConnState(childName, balancer.ClientConnState{
			ResolverState: resolver.State{
				Endpoints:     endpointsSplit[childName],
				ServiceConfig: s.ResolverState.ServiceConfig,
				Attributes:    s.ResolverState.Attributes,
			},
			BalancerConfig: lbCfg,
		}); err != nil {
			retErr = fmt.Errorf("failed to push new configuration %v to child %q: %v", childCfg.ChildPolicy.Config, childName, err)
			b.setErrorPickerForChild(childName, retErr)
		}

View on GitHub (pinned to 0c51461d27)