grpc/grpc-go · error

produced zero addresses

Error message

produced zero addresses

What it means

Thrown when the *anypb.Any for the Router filter fails to unmarshal into *pb.Router. The payload bytes do not form a valid Router proto — either the type_url is wrong, the bytes are corrupt, or there is a go-control-plane schema version mismatch between the control plane and grpc-go.

Source

Thrown at balancer/pickfirst/pickfirst.go:219

		return
	}

	b.updateBalancerState(balancer.State{
		ConnectivityState: connectivity.TransientFailure,
		Picker:            &picker{err: fmt.Errorf("name resolver error: %v", err)},
	})
}

func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState) error {
	b.mu.Lock()
	defer b.mu.Unlock()
	b.cancelConnectionTimer()
	if len(state.ResolverState.Addresses) == 0 && len(state.ResolverState.Endpoints) == 0 {
		// Cleanup state pertaining to the previous resolver state.
		// Treat an empty address list like an error by calling b.ResolverError.
		b.closeSubConnsLocked()
		b.addressList.updateAddrs(nil)
		b.resolverErrorLocked(errors.New("produced zero addresses"))
		return balancer.ErrBadResolverState
	}
	b.healthCheckingEnabled = state.ResolverState.Attributes.Value(enableHealthListenerKeyType{}) != nil
	cfg, ok := state.BalancerConfig.(pfConfig)
	if state.BalancerConfig != nil && !ok {
		return fmt.Errorf("pickfirst: received illegal BalancerConfig (type %T): %v: %w", state.BalancerConfig, state.BalancerConfig, balancer.ErrBadResolverState)
	}

	if b.logger.V(2) {
		b.logger.Infof("Received new config %s, resolver state %s", pretty.ToJSON(cfg), pretty.ToJSON(state.ResolverState))
	}

	var newAddrs []resolver.Address
	if endpoints := state.ResolverState.Endpoints; len(endpoints) != 0 {
		// Perform the optional shuffling described in gRFC A62. The shuffling
		// will change the order of endpoints but not touch the order of the
		// addresses within each endpoint. - A61
		if cfg.ShuffleAddressList {

View on GitHub (pinned to 0c51461d27)

Solutions

  1. Confirm the Any type_url is type.googleapis.com/envoy.extensions.filters.http.router.v3.Router and the payload is a valid (possibly empty) Router message.
  2. Align go-control-plane versions between the control plane and the grpc-go data plane.
  3. Decode the Any payload with protoc --decode_raw or grpcurl to inspect the actual bytes, and fix the control-plane serialization.

Example fix

// before: wrong type_url in the Any
any := &anypb.Any{
    TypeUrl: "type.googleapis.com/envoy.extensions.filters.http.router.v3.RouterConfig",
    Value:   rawBytes,
}

// after:
any, _ := anypb.New(&pb.Router{})  // correct type_url auto-set
Defensive patterns

Strategy: validation

Validate before calling

// Validate the Router filter Any before parsing:
func validateRouterAny(any *anypb.Any) error {
    expected := "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
    if any.TypeUrl != expected {
        return fmt.Errorf("router type_url mismatch: want %s, got %s", expected, any.TypeUrl)
    }
    msg := new(pb.Router)
    return any.UnmarshalTo(msg)
}

Prevention

When it happens

Trigger: The Any's type_url does not match type.googleapis.com/envoy.extensions.filters.http.router.v3.Router, or the serialized bytes correspond to a different proto version of Router. A DiscoveryResponse with a corrupt or truncated filter config payload. A control plane using a newer Router proto with unknown fields that fail strict unmarshalling.

Common situations: Control plane upgraded go-control-plane to a version with breaking proto changes without upgrading grpc-go. A hand-crafted LDS resource with a copy-paste type_url from a different filter. Network-level corruption of the xDS DiscoveryResponse.

Related errors


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