grpc/grpc-go · error
failed to JSON marshal load balancing policy for child
Error message
failed to JSON marshal load balancing policy for child %q: %v
What it means
When a child policy's type changes in cluster_manager (clustermanager.go:113-122), the new ChildPolicy must be re-serialized to JSON so balancergroup.ParseConfig can drive a graceful switch. If childCfg.ChildPolicy.MarshalJSON fails, cluster_manager records the error, installs an error picker for that one child via setErrorPickerForChild, and continues processing the rest. Only RPCs routed to the affected child fail.
Solutions
- Inspect the raw bytes of ChildPolicy.Config for malformed JSON before deployment.
- If using a custom balancer, ensure MarshalJSON never errors on valid in-memory configs.
- Treat this child as broken: cluster_manager isolates the failure, so other children keep serving while you fix the source.
Defensive patterns
Strategy: try-catch
Try / catch
// UpdateClientConnState on cluster_manager returns the wrapped error; isolate per-child failures.
if err := cm.UpdateClientConnState(state); err != nil {
if strings.Contains(err.Error(), "failed to JSON marshal load balancing policy for child") {
// log the child name from the error, continue serving other children
}
} Prevention
- Validate that every ChildPolicy.Config is valid JSON before sending it to cluster_manager.
- For custom child balancers, ensure MarshalJSON never errors on in-memory configs.
- Use channelz to correlate which child failed.
When it happens
Trigger: childCfg.ChildPolicy.Config holds a json.RawMessage with corrupt/invalid bytes, or a custom ChildPolicy type whose MarshalJSON returns an error during the policy-type-change path (newPolicyName != oldPolicyName).
Common situations: Hand-crafted ChildPolicy.Config with invalid raw JSON bytes; bug in a custom child balancer's MarshalJSON; rare race in xDS resource translation producing malformed raw messages.
Related errors
- failed to parse load balancing policy for child
- failed to push new configuration
- unexpected balancer config with type: %T
- xds: unable to unmarshal LBconfig
- child policy not registered
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/0348f842c22b4cd6.
Report an issue: GitHub.
Appendix: source
Thrown at internal/xds/balancer/clustermanager/clustermanager.go:119
// Add new sub-balancers to the aggregator and balancergroup.
b.stateAggregator.add(childName)
b.bg.Add(childName, balancer.Get(childCfg.ChildPolicy.Name))
} else {
// If the child policy type has changed for existing sub-balancers,
// parse the new config and send down the config update to the
// balancergroup, which will take care of gracefully switching the
// child over to the new policy.
//
// If we run into errors here, we need to ensure that RPCs to this
// 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{View on GitHub (pinned to 0c51461d27)