grpc/grpc-go · error
xds: unable to unmarshal lbconfig
Error message
xds: unable to unmarshal lbconfig: %s, error: %v
What it means
Returned by bb.ParseConfig (line 112) when json.Unmarshal fails on the raw JSON load balancer config passed to the CDS balancer's config parser. The %s shows the raw JSON input and %v shows the unmarshalling error. This config is the {"cluster": "...", "isDynamic": ...} structure defined by the lbConfig struct at lines 101-105.
Solutions
- Inspect the full error message which includes the raw JSON (%s) — the malformed portion is usually visible there.
- Validate that the JSON matches {"cluster": "<string>", "isDynamic": <bool>} exactly.
- Do not hand-construct CDS balancer config; rely on the xDS resolver to generate it.
- In tests, use json.Marshal on a properly constructed lbConfig struct rather than string concatenation.
Example fix
// before: hand-built JSON with a typo
raw := []byte(`{"cluster":"my-cluster","isDyanmic":true}`) // 'isDyanmic' misspelled
// after: construct via struct + json.Marshal
cfg := lbConfig{ClusterName: "my-cluster", IsDynamic: true}
raw, _ := json.Marshal(cfg) Defensive patterns
Strategy: validation
Validate before calling
// Validate CDS LB config JSON before passing it to ParseConfig
func validateCDSConfig(raw json.RawMessage) error {
var cfg struct {
ClusterName string `json:"cluster"`
IsDynamic bool `json:"isDynamic"`
}
if err := json.Unmarshal(raw, &cfg); err != nil {
return fmt.Errorf("invalid CDS config JSON: %w", err)
}
if cfg.ClusterName == "" {
return fmt.Errorf("CDS config missing 'cluster' field")
}
return nil
} Try / catch
// Go: check the error from ParseConfig
cfg, err := cdsBuilder.ParseConfig(rawJSON)
if err != nil {
if strings.Contains(err.Error(), "unable to unmarshal lbconfig") {
log.Printf("CDS config JSON is invalid: %s", rawJSON)
// fix the JSON source
}
} Prevention
- Never hand-construct balancer config JSON; always use json.Marshal on typed structs.
- In tests, validate the JSON with a schema check before passing to ParseConfig.
- Log the raw JSON on parse failure for easier debugging.
When it happens
Trigger: The xDS resolver (or a test harness) calls the cds_experimental balancer's ParseConfig with malformed JSON, or JSON whose 'cluster'/'isDynamic' fields have wrong types. In production this is generated by the xdsResolver so should be well-formed; in tests or custom resolvers it can be passed bad data.
Common situations: A custom service config or test that constructs the CDS LB config JSON by hand with a syntax error or type mismatch (e.g. isDynamic as a string instead of bool). A version mismatch where the lbConfig struct fields changed but the caller sends old-format JSON.
Related errors
- error parsing Outlier Detection config
- error unmarshalling xDS LB Policy
- did not find the cluster
- failed to correctly update Outlier Detection config
- failed to parse load balancing policy for child
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/09116ac3c8155dd8.
Report an issue: GitHub.
Appendix: 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 0c51461d27)