grpc/grpc-go · error

balancergroup: unregistered balancer name %q

Error message

balancergroup: unregistered balancer name %q

What it means

Returned by BalancerGroup.AddWithClientConn when balancer.Get(balancerName) returns nil at balancergroup.go:275-278. The requested balancer name is not registered in the global balancer registry. BalancerGroup uses balancer.Get to look up the builder by name, and if no builder was registered under that name, it cannot construct the sub-balancer.

Source

Thrown at internal/balancergroup/balancergroup.go:277

		logger:          opts.Logger,

		deletedBalancerCache: bc,
		idToBalancerConfig:   make(map[string]*subBalancerWrapper),
		scToSubBalancer:      make(map[balancer.SubConn]*subBalancerWrapper),
	}
}

// AddWithClientConn adds a balancer with the given id to the group. The
// balancer is built with a balancer builder registered with balancerName. The
// given ClientConn is passed to the newly built balancer instead of the
// one passed to balancergroup.New().
//
// TODO: Get rid of the existing Add() API and replace it with this.
func (bg *BalancerGroup) AddWithClientConn(id, balancerName string, cc balancer.ClientConn) error {
	bg.logger.Infof("Adding child policy of type %q for child %q", balancerName, id)
	builder := balancer.Get(balancerName)
	if builder == nil {
		return fmt.Errorf("balancergroup: unregistered balancer name %q", balancerName)
	}

	// Store data in static map, and then check to see if bg is started.
	bg.outgoingMu.Lock()
	defer bg.outgoingMu.Unlock()
	if bg.outgoingClosed {
		return fmt.Errorf("balancergroup: already closed")
	}
	var sbc *subBalancerWrapper
	// Skip searching the cache if disabled.
	if bg.deletedBalancerCache != nil {
		if old, ok := bg.deletedBalancerCache.Remove(id); ok {
			if bg.logger.V(2) {
				bg.logger.Infof("Removing and reusing child policy of type %q for child %q from the balancer cache", balancerName, id)
				bg.logger.Infof("Number of items remaining in the balancer cache: %d", bg.deletedBalancerCache.Len())
			}

			sbc, _ = old.(*subBalancerWrapper)

View on GitHub (pinned to 0c51461d27)

Solutions

  1. Register the balancer before adding it: balancer.Register(yourBuilder) or import the package for its init side effect
  2. Verify the name is registered: if balancer.Get(name) == nil { /* not registered */ }
  3. Use builder.Name() to pass the exact registered name rather than hardcoding a string
  4. Check for typos against the official balancer name constants

Example fix

// before: name not registered
bg.AddWithClientConn("child-0", "my_lb", cc)

// after: register first
balancer.Register(myBuilder)
bg.AddWithClientConn("child-0", myBuilder.Name(), cc)
Defensive patterns

Strategy: validation

Validate before calling

// Verify balancer name is registered before calling AddWithClientConn
func safeAdd(bg *balancergroup.BalancerGroup, id, name string, cc balancer.ClientConn) error {
    if balancer.Get(name) == nil {
        return fmt.Errorf("balancer %q is not registered; import the package first", name)
    }
    return bg.AddWithClientConn(id, name, cc)
}

Prevention

When it happens

Trigger: Calling bg.AddWithClientConn(id, "some_name", cc) or bg.Add(id, builder) (which calls AddWithClientConn with builder.Name()) where 'some_name' was never registered via balancer.Register(). For example, passing 'my_custom_lb' without first registering it.

Common situations: A custom balancer that was built but never registered with balancer.Register. Using the wrong name string (e.g., 'weighted_round_robin' vs 'weighted-target'). Forgetting to import a balancer package that registers itself in init().

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/ca6306aaa951e9f7. Report an issue: GitHub.