{"record":{"id":"45aa36ecef4a2a22","repo":"grpc/grpc-go","slug":"q-lb-policy-is-needed-but-not-registered","errorCode":null,"errorMessage":"%q LB policy is needed but not registered","messagePattern":"%q LB policy is needed but not registered","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/balancer/cdsbalancer/cdsbalancer.go","lineNumber":73,"sourceCode":")\n\nfunc init() {\n\tbalancer.Register(bb{})\n}\n\n// bb implements the balancer.Builder interface to help build a cdsBalancer.\n// It also implements the balancer.ConfigParser interface to help parse the\n// JSON service config, to be passed to the cdsBalancer.\ntype bb struct{}\n\n// Build creates a new CDS balancer with the ClientConn.\nfunc (bb) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer {\n\tbuilder := balancer.Get(priority.Name)\n\tif builder == nil {\n\t\t// Shouldn't happen, registered through imported Priority builder. Still,\n\t\t// defensive programming.\n\t\tlogger.Errorf(\"%q LB policy is needed but not registered\", priority.Name)\n\t\treturn nop.NewBalancer(cc, fmt.Errorf(\"%q LB policy is needed but not registered\", priority.Name))\n\t}\n\tparser, ok := builder.(balancer.ConfigParser)\n\tif !ok {\n\t\t// Shouldn't happen, imported Priority builder has this method.\n\t\tlogger.Errorf(\"%q LB policy does not implement a config parser\", priority.Name)\n\t\treturn nop.NewBalancer(cc, fmt.Errorf(\"%q LB policy does not implement a config parser\", priority.Name))\n\t}\n\n\tb := &cdsBalancer{\n\t\tbOpts:             opts,\n\t\tchildConfigParser: parser,\n\t\tclusterConfigs:    make(map[string]*xdsresource.ClusterResult),\n\t\tpriorityConfigs:   make(map[string]*priorityConfig),\n\t\tcc:                cc,\n\t}\n\tb.logger = prefixLogger(b)\n\tb.logger.Infof(\"Created\")\n\treturn b","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/xds/balancer/cdsbalancer/cdsbalancer.go#L55-L91","documentation":"Logged and wrapped in a nop balancer error in bb.Build() (line 72-73) when balancer.Get(priority.Name) returns nil at build time. This is a duplicate defensive check of error 341 but at the Build() entry point rather than the newChildBalancer factory. The balancer is placed into a nop (no-op) state with this error as its reason, causing the channel to report TRANSIENT_FAILURE.","triggerScenarios":"The CDS balancer builder's Build() method is called by gRPC's balancer switching logic, and the priority balancer is not registered in the global registry. Identical root cause as error 341 but hit through a different code path — at initial balancer construction rather than during child balancer creation.","commonSituations":"Same as 341: missing priority package import, inconsistent grpc-go versions, or custom builds that strip the dependency. The comment at line 70-71 explicitly says 'Shouldn't happen, registered through imported Priority builder.'","solutions":["Import the full xds package so the priority balancer is registered: import _ \"google.golang.org/grpc/xds\".","Check for version mismatches in go.mod: run 'go mod graph | grep grpc' and ensure no conflicting versions.","If this appears in tests, ensure your test binary imports the priority package or uses grpc.NewClient with the xds resolver properly initialized."],"exampleFix":"// before: partial import causing unregistered priority builder\nimport (\n    _ \"google.golang.org/grpc/internal/xds/balancer/cdsbalancer\"\n)\n\n// after: public xds import registers all dependent balancers\nimport (\n    _ \"google.golang.org/grpc/xds\"\n)","handlingStrategy":"validation","validationCode":"// Startup check: verify required balancers are registered\nfunc validateXDSBalancers() error {\n    required := []string{\"cds_experimental\", \"priority_experimental\"}\n    for _, name := range required {\n        if balancer.Get(name) == nil {\n            return fmt.Errorf(\"balancer %q not registered\", name)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// After channel creation, check connectivity state for TRANSIENT_FAILURE\nstate := conn.GetState()\nif state == connectivity.TransientFailure {\n    // may indicate an unregistered balancer; check logs for the nop balancer error\n}","preventionTips":["Use the public xds package import to ensure all balancers are registered.","Add integration tests that create an xDS channel and verify it reaches READY.","Check for balancer registration at startup to fail fast with a clear message."],"tags":["xds","balancer","registration","priority","build"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}