grpc/grpc-go · error

produced zero addresses

Error message

produced zero addresses

What it means

Thrown by the Router HTTP filter builder's ParseFilterConfig when the base proto.Message config is nil. The Router filter is the terminal filter in an xDS HTTP connection manager and must always have a (possibly empty) configuration. A nil value means the LDS resource omitted the typed_config for the Router filter entirely even though it was listed in http_filters.

Source

Thrown at balancer/base/balancer.go:138

			b.csEvltr.RecordTransition(connectivity.Shutdown, connectivity.Idle)
			sc.Connect()
		}
	}
	for a, sc := range b.subConns.All() {
		// a was removed by resolver.
		if _, ok := addrsSet.Get(a); !ok {
			sc.Shutdown()
			b.subConns.Delete(a)
			// Keep the state of this sc in b.scStates until sc's state becomes Shutdown.
			// The entry will be deleted in updateSubConnState.
		}
	}
	// If resolver state contains no addresses, return an error so ClientConn
	// will trigger re-resolve. Also records this as a resolver error, so when
	// the overall state turns transient failure, the error message will have
	// the zero address information.
	if len(s.ResolverState.Addresses) == 0 {
		b.ResolverError(errors.New("produced zero addresses"))
		return balancer.ErrBadResolverState
	}

	b.regeneratePicker()
	b.cc.UpdateState(balancer.State{ConnectivityState: b.state, Picker: b.picker})
	return nil
}

// mergeErrors builds an error from the last connection error and the last
// resolver error.  Must only be called if b.state is TransientFailure.
func (b *baseBalancer) mergeErrors() error {
	// connErr must always be non-nil unless there are no SubConns, in which
	// case resolverErr must be non-nil.
	if b.connErr == nil {
		return fmt.Errorf("last resolver error: %v", b.resolverErr)
	}
	if b.resolverErr == nil {
		return fmt.Errorf("last connection error: %v", b.connErr)

View on GitHub (pinned to 0c51461d27)

Solutions

  1. Add a typed_config to the router filter entry in the LDS resource: type_url type.googleapis.com/envoy.extensions.filters.http.router.v3.Router with an empty Router message body.
  2. If using a control plane like Istio, ensure the gateway/listener template includes the router filter with its full typed config.
  3. For local testing, construct the LDS HttpFilter with cfg set to an *anypb.Any wrapping an empty pb.Router{}.

Example fix

// before:
httpFilters = append(httpFilters, &v3listenerpb.HttpFilter{
    Name: "envoy.filters.http.router",
    // IsTerminal will not recognize it; typed_config missing
})

// after:
routerAny, _ := anypb.New(&pb.Router{})
httpFilters = append(httpFilters, &v3listenerpb.HttpFilter{
    Name:         "envoy.filters.http.router",
    ConfigType: &v3listenerpb.HttpFilter_TypedConfig{TypedConfig: routerAny},
})
Defensive patterns

Strategy: validation

Validate before calling

// Validate every http_filter in an LDS resource has a non-nil typed_config:
for _, hf := range listener.GetApiListener().GetHttpFilters() {
    if hf.GetTypedConfig() == nil && hf.GetIsOptional() == false {
        if isRouterFilterName(hf.GetName()) {
            return fmt.Errorf("router filter %q has no typed_config", hf.GetName())
        }
    }
}

Prevention

When it happens

Trigger: An LDS resource lists {name: "envoy.filters.http.router"} in its http_filters array but does not supply a typed_config (or supplies a nil one). The xdsclient code that populates http_filters passes nil when the typed_config field of a HttpFilter proto is absent.

Common situations: A manually-crafted LDS resource (e.g., for testing or from a minimal control plane) that names the router filter but forgets its typed_config. A Traffic Director or Istio configuration that lists the router filter with a config_source but no inline typed config. Migration from v2 to v3 xDS where the filter config encoding changed.

Related errors


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