grpc/grpc-go · error
error unmarshalling xDS LB Policy
Error message
error unmarshalling xDS LB Policy: %v
What it means
Returned by handleClusterUpdate (line 256, wrapped via annotateErrorWithNodeID) when json.Unmarshal fails on clusterConfig.Cluster.LBPolicy (a json.RawMessage from the CDS cluster resource) into b.xdsLBPolicy (an internalserviceconfig.BalancerConfig). The %v is the JSON unmarshalling error. This means the LB policy field in the cluster resource contains malformed or structurally incompatible JSON.
Solutions
- Inspect the CDS resource's lb_policy field in the management server config and validate it is a recognized policy name (round_robin, ring_hash, xds_wrr_locality, least_request).
- Check for version compatibility between the client grpc-go version and the control plane — newer LB policies may not be supported by older clients.
- Enable xDS debug logging and look at the pretty-printed cluster resource to see the raw LBPolicy bytes.
- Update the client to a grpc-go version that supports the LB policy the server is configuring.
Defensive patterns
Strategy: validation
Validate before calling
// If you control the cluster resource, validate the LB policy JSON
// before sending it via CDS:
func validateLBPolicy(raw json.RawMessage) error {
var cfg internalserviceconfig.BalancerConfig
if err := json.Unmarshal(raw, &cfg); err != nil {
return fmt.Errorf("invalid LB policy JSON: %w", err)
}
validPolicies := map[string]bool{
"round_robin": true, "ring_hash": true,
"xds_wrr_locality": true, "least_request": true,
}
if !validPolicies[cfg.Name] {
return fmt.Errorf("unsupported LB policy: %s", cfg.Name)
}
return nil
} Try / catch
// Channel will enter TRANSIENT_FAILURE. Monitor and inspect logs:
if conn.GetState() == connectivity.TransientFailure {
// grep logs for 'error unmarshalling xDS LB Policy'
// the %v shows the JSON error pointing to the bad field
} Prevention
- Use only documented, client-supported LB policies in the management server cluster config.
- Version-check the client grpc-go release notes for newly supported LB policies.
- Test cluster resources in staging before production rollout.
When it happens
Trigger: The management server's CDS response includes a cluster with an LB policy field (typically {"xds_wrr_locality":{}} or {"ring_hash":{}}) that does not match the BalancerConfig struct shape. This fires after the cluster resource is accepted but before child config is built.
Common situations: Management server sends an unrecognized or malformed LB policy JSON in the cluster resource. Version skew: the server sends a new LB policy format the client does not understand. A control plane bug producing invalid JSON in the LBPolicy field. The LB policy field is present but empty or not a valid JSON object.
Related errors
- xds: unable to unmarshal lbconfig
- did not find the cluster
- error parsing Outlier Detection config
- failed to correctly update Outlier Detection config
- failed to unmarshal config
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/72c3d115f197225b.
Report an issue: GitHub.
Appendix: source
Thrown at internal/xds/balancer/cdsbalancer/cdsbalancer.go:256
p := b.updatePriorityConfig(clusterName, &clusterConfig)
newPriorities = append(newPriorities, p)
case xdsresource.ClusterTypeAggregate:
for _, leaf := range clusterConfig.AggregateConfig.LeafClusters {
leafCluster := b.clusterConfigs[leaf]
// Update priority config for leaf clusters.
p := b.updatePriorityConfig(leaf, &leafCluster.Config)
newPriorities = append(newPriorities, p)
}
}
b.priorities = newPriorities
if err := b.updateOutlierDetection(); err != nil {
return b.annotateErrorWithNodeID(fmt.Errorf("failed to correctly update Outlier Detection config %v", err))
}
// The LB policy is configured by the root cluster.
if err := json.Unmarshal(clusterConfig.Cluster.LBPolicy, &b.xdsLBPolicy); err != nil {
return b.annotateErrorWithNodeID(fmt.Errorf("error unmarshalling xDS LB Policy: %v", err))
}
if err := b.updateChildConfig(); err != nil {
return b.annotateErrorWithNodeID(err)
}
return nil
}
// updateChildConfig builds child policy configuration using endpoint addresses
// returned from the XDSConfig and child policy configuration.
//
// A child policy is created if one doesn't already exist. The newly built
// configuration is then pushed to the child policy.
func (b *cdsBalancer) updateChildConfig() error {
if b.childLB == nil {
childLB, err := newChildBalancer(b.cc, b.bOpts)
if err != nil {
return fmt.Errorf("failed to create child policy of type %s: %v", priority.Name, err)
}View on GitHub (pinned to 0c51461d27)