{"record":{"id":"d7c69856b0b94a67","repo":"grpc/grpc-go","slug":"unexpected-balancer-config-with-type-t-d7c698","errorCode":null,"errorMessage":"unexpected balancer config with type: %T","messagePattern":"unexpected balancer config with type: %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/balancer/clustermanager/clustermanager.go","lineNumber":173,"sourceCode":"\t// If multiple sub-balancers run into errors, we will return only the last\n\t// one, which is still good enough, since the grpc channel will anyways\n\t// return this error as balancer.ErrBadResolver to the name resolver,\n\t// resulting in re-resolution attempts.\n\treturn retErr\n\n\t// Adding or removing a sub-balancer will result in the\n\t// needUpdateStateOnResume bit to true which results in a picker update once\n\t// resumeStateUpdates() is called.\n}\n\nfunc (b *bal) UpdateClientConnState(s balancer.ClientConnState) error {\n\tif b.logger.V(2) {\n\t\tb.logger.Infof(\"Received update from resolver, balancer config: %+v\", pretty.ToJSON(s.BalancerConfig))\n\t}\n\n\tnewConfig, ok := s.BalancerConfig.(*lbConfig)\n\tif !ok {\n\t\treturn fmt.Errorf(\"unexpected balancer config with type: %T\", s.BalancerConfig)\n\t}\n\n\tb.stateAggregator.pauseStateUpdates()\n\tdefer b.stateAggregator.resumeStateUpdates()\n\treturn b.updateChildren(s, newConfig)\n}\n\nfunc (b *bal) ResolverError(err error) {\n\tb.bg.ResolverError(err)\n}\n\nfunc (b *bal) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {\n\tb.logger.Errorf(\"UpdateSubConnState(%v, %+v) called unexpectedly\", sc, state)\n}\n\nfunc (b *bal) Close() {\n\tb.stateAggregator.close()\n\tb.bg.Close()","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/xds/balancer/clustermanager/clustermanager.go#L155-L191","documentation":"cluster_manager's UpdateClientConnState asserts s.BalancerConfig is *clustermanager.lbConfig (clustermanager.go:171-174). Any other type is rejected because cluster_manager needs the Children map and per-child policy configs to drive its sub-balancers via balancergroup.","triggerScenarios":"UpdateClientConnState called on cluster_manager with a non-*lbConfig BalancerConfig; the xDS resolver produced the wrong shape or a custom parent bypassed clustermanager.ParseConfig.","commonSituations":"Custom resolver that doesn't use clustermanager.ParseConfig; gRPC version drift; tests that pass a raw struct.","solutions":["Always route config through clustermanager.ParseConfig(rawJSON) to produce *lbConfig.","Ensure the resolver's service config produces the xds_cluster_manager_experimental policy with the expected JSON children/priorities shape.","Check that the parent policy sends the exact *lbConfig returned by ParseConfig, not a wrapped or translated type."],"exampleFix":"// before\nb.UpdateClientConnState(balancer.ClientConnState{\n    BalancerConfig: &someOtherConfig{},\n})\n// after\nimport \"google.golang.org/grpc/internal/xds/balancer/clustermanager\"\ncfg, err := clustermanager.ParseConfig(rawJSON)\nif err != nil { return err }\nb.UpdateClientConnState(balancer.ClientConnState{BalancerConfig: cfg})","handlingStrategy":"type-guard","validationCode":"func validateClusterManagerConfig(cfg serviceconfig.LoadBalancingConfig) error {\n    // *clustermanager.lbConfig is unexported; the public route is ParseConfig.\n    // Round-trip check: marshal then ParseConfig again.\n    raw, err := json.Marshal(cfg)\n    if err != nil { return err }\n    _, err = clustermanager.ParseConfig(raw)\n    return err\n}","typeGuard":"// lbConfig is unexported, so callers cannot name it. Use ParseConfig to obtain the only valid value.\n// In tests you can rely on the fact that bb.ParseConfig returns the only accepted type.","tryCatchPattern":null,"preventionTips":["Always produce the config via clustermanager.ParseConfig rather than constructing it manually.","Do not translate the config into a different type between resolver and balancer.","Pin resolver and balancer packages to the same gRPC-go version."],"tags":["go","grpc","xds","balancer","config-type","clustermanager"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}