{"record":{"id":"0348f842c22b4cd6","repo":"grpc/grpc-go","slug":"failed-to-json-marshal-load-balancing-policy-for-c","errorCode":null,"errorMessage":"failed to JSON marshal load balancing policy for child %q: %v","messagePattern":"failed to JSON marshal load balancing policy for child %q: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/balancer/clustermanager/clustermanager.go","lineNumber":119,"sourceCode":"\t\t\t// Add new sub-balancers to the aggregator and balancergroup.\n\t\t\tb.stateAggregator.add(childName)\n\t\t\tb.bg.Add(childName, balancer.Get(childCfg.ChildPolicy.Name))\n\t\t} else {\n\t\t\t// If the child policy type has changed for existing sub-balancers,\n\t\t\t// parse the new config and send down the config update to the\n\t\t\t// balancergroup, which will take care of gracefully switching the\n\t\t\t// child over to the new policy.\n\t\t\t//\n\t\t\t// If we run into errors here, we need to ensure that RPCs to this\n\t\t\t// child fail, while RPCs to other children with good configs\n\t\t\t// continue to succeed.\n\t\t\tnewPolicyName, oldPolicyName := childCfg.ChildPolicy.Name, b.children[childName].ChildPolicy.Name\n\t\t\tif newPolicyName != oldPolicyName {\n\t\t\t\tvar err error\n\t\t\t\tvar cfgJSON []byte\n\t\t\t\tcfgJSON, err = childCfg.ChildPolicy.MarshalJSON()\n\t\t\t\tif err != nil {\n\t\t\t\t\tretErr = fmt.Errorf(\"failed to JSON marshal load balancing policy for child %q: %v\", childName, err)\n\t\t\t\t\tb.setErrorPickerForChild(childName, retErr)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// This overwrites lbCfg to be in the format expected by the\n\t\t\t\t// gracefulswitch balancer. So, when this config is pushed to\n\t\t\t\t// the child (below), it will result in a graceful switch to the\n\t\t\t\t// new child policy.\n\t\t\t\tlbCfg, err = balancergroup.ParseConfig(cfgJSON)\n\t\t\t\tif err != nil {\n\t\t\t\t\tretErr = fmt.Errorf(\"failed to parse load balancing policy for child %q: %v\", childName, err)\n\t\t\t\t\tb.setErrorPickerForChild(childName, retErr)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif err := b.bg.UpdateClientConnState(childName, balancer.ClientConnState{\n\t\t\tResolverState: resolver.State{","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/xds/balancer/clustermanager/clustermanager.go#L101-L137","documentation":"The cluster manager balancer handles route-action-based cluster selection (xDS RDS routing). When a child's policy type changes, it marshals the new child policy config to JSON for the gracefulswitch balancer. This error fires when that JSON marshal fails, meaning the child policy's config struct cannot be serialized. This prevents a graceful switch to the new policy for that specific child.","triggerScenarios":"Triggered in cluster_manager's UpdateClientConnState when a child's policy name changed and childCfg.ChildPolicy.MarshalJSON() fails. The error is scoped to one child — other children with good configs continue functioning. The child policy config comes from the xDS route configuration.","commonSituations":"The child policy config struct has a custom MarshalJSON that fails (programming bug in a custom balancer); the config contains an unsupported Go type that json.Marshal rejects; a version mismatch where the child policy config struct changed incompatibly; a nil or incomplete config struct passed from the xDS route configuration.","solutions":["Enable GRPC_GO_LOG_SEVERITY=info to identify which child is failing (the child name is in the error message)","Verify the route configuration on the management server specifies valid and complete LB policy configs for all routes","Upgrade grpc-go to ensure all child policy config structs are compatible","If using custom balancers, check their MarshalJSON implementation for bugs","The error is isolated to one child — other children continue to work, so verify if the failing child's config is critical"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Validate that child policy configs can marshal to JSON\n// (relevant if constructing configs programmatically; normally xDS handles this)\nfunc canMarshalChildPolicy(cfg *internalserviceconfig.BalancerConfig) bool {\n    _, err := cfg.MarshalJSON()\n    return err == nil\n}","typeGuard":null,"tryCatchPattern":"// This error is isolated to one child; other children continue working\n// Monitor per-route RPC success rates to detect affected routes","preventionTips":["Validate management server route configurations for valid LB policy configs","Test route config changes in staging before production","Keep grpc-go version current for balancer config compatibility","Monitor per-cluster RPC success rates to detect isolated failures"],"tags":["xds","clustermanager","json","child-policy","gracefulswitch"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}