grpc/grpc-go · error

xds: unable to unmarshal lbconfig: %s, error: %v

Error message

xds: unable to unmarshal lbconfig: %s, error: %v

What it means

The CDS (Cluster Discovery Service) balancer's ParseConfig method attempts to JSON-unmarshal the load balancing config into a struct expecting fields {cluster, isDynamic}. This error fires when the JSON is syntactically invalid or field types are wrong. The CDS balancer is the top-level xDS cluster policy that resolves cluster resources from the management server.

Source

Thrown at internal/xds/balancer/cdsbalancer/cdsbalancer.go:112

// Name returns the name of balancers built by this builder.
func (bb) Name() string {
	return cdsName
}

// lbConfig represents the loadBalancingConfig section of the service config
// for the cdsBalancer.
type lbConfig struct {
	serviceconfig.LoadBalancingConfig
	ClusterName string `json:"cluster"`
	IsDynamic   bool   `json:"isDynamic"`
}

// ParseConfig parses the JSON load balancer config provided into an
// internal form or returns an error if the config is invalid.
func (bb) ParseConfig(c json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
	var cfg lbConfig
	if err := json.Unmarshal(c, &cfg); err != nil {
		return nil, fmt.Errorf("xds: unable to unmarshal lbconfig: %s, error: %v", string(c), err)
	}
	return &cfg, nil
}

// cdsBalancer implements a CDS based LB policy. It instantiates a
// cluster_resolver balancer to further resolve the serviceName received from
// CDS, into localities and endpoints. Implements the balancer.Balancer
// interface which is exposed to gRPC and implements the balancer.ClientConn
// interface which is exposed to the cluster_resolver balancer.
type cdsBalancer struct {
	// The following fields are initialized at build time and are either
	// read-only after that or provide their own synchronization, and therefore
	// do not need to be guarded by a mutex.
	cc                balancer.ClientConn   // ClientConn interface passed to child LB.
	bOpts             balancer.BuildOptions // BuildOptions passed to child LB.
	childConfigParser balancer.ConfigParser // Config parser for cluster_resolver LB policy.
	logger            *grpclog.PrefixLogger // Prefix logger for all logging.

View on GitHub (pinned to 03255a9237)

Solutions

  1. Inspect the full error message which includes the raw JSON that failed to parse — it will reveal the exact malformed input
  2. Verify the xDS management server is sending valid JSON with correct field types (cluster as string, isDynamic as boolean)
  3. Check for version compatibility between your gRPC client and the xDS server protocol version
  4. Validate that the service config's loadBalancingConfig for cds_experimental matches the expected schema: {"cluster": "<name>", "isDynamic": <bool>}

Example fix

// before: malformed service config with wrong types
{"loadBalancingConfig": [{"cds_experimental": {"cluster": "my_cluster", "isDynamic": "true"}}]}
// after: correct types
{"loadBalancingConfig": [{"cds_experimental": {"cluster": "my_cluster", "isDynamic": true}}]}
Defensive patterns

Strategy: validation

Validate before calling

// Validate CDS LB config JSON before passing to xDS
func validateCDSConfig(rawJSON json.RawMessage) error {
    var cfg struct {
        Cluster  string `json:"cluster"`
        IsDynamic bool  `json:"isDynamic"`
    }
    if err := json.Unmarshal(rawJSON, &cfg); err != nil {
        return fmt.Errorf("invalid CDS config JSON: %w", err)
    }
    if cfg.Cluster == "" {
        return fmt.Errorf("CDS config missing cluster name")
    }
    return nil
}

Type guard

// Type guard for parsed CDS config
func isCDSConfig(cfg serviceconfig.LoadBalancingConfig) bool {
    _, ok := cfg.(*cdsbalancer.LBConfig) // or via reflection
    return ok
}

Prevention

When it happens

Trigger: Triggered when gRPC's xDS resolver parses the service config's loadBalancingConfig section for the cds_experimental policy and json.Unmarshal fails on the raw JSON. Happens when the xDS server sends a service config with missing required fields, wrong types for cluster/isDynamic, or malformed JSON.

Common situations: Management server (Istio/Envoy/Traffic Director) sends an invalid or incomplete CDS load balancing config; version mismatch between the xDS server and the gRPC client where the server sends new fields the client doesn't understand; bootstrap or service config misconfiguration where the cluster name is empty or isDynamic is a string instead of bool.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/09116ac3c8155dd8. Report an issue: GitHub.