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

  1. 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).
  2. Check for version compatibility between the client grpc-go version and the control plane — newer LB policies may not be supported by older clients.
  3. Enable xDS debug logging and look at the pretty-printed cluster resource to see the raw LBPolicy bytes.
  4. 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

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


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)