{"record":{"id":"bf0a4d2791476cdb","repo":"grpc/grpc-go","slug":"produced-zero-addresses","errorCode":null,"errorMessage":"produced zero addresses","messagePattern":"produced zero addresses","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"balancer/base/balancer.go","lineNumber":138,"sourceCode":"\t\t\tb.csEvltr.RecordTransition(connectivity.Shutdown, connectivity.Idle)\n\t\t\tsc.Connect()\n\t\t}\n\t}\n\tfor a, sc := range b.subConns.All() {\n\t\t// a was removed by resolver.\n\t\tif _, ok := addrsSet.Get(a); !ok {\n\t\t\tsc.Shutdown()\n\t\t\tb.subConns.Delete(a)\n\t\t\t// Keep the state of this sc in b.scStates until sc's state becomes Shutdown.\n\t\t\t// The entry will be deleted in updateSubConnState.\n\t\t}\n\t}\n\t// If resolver state contains no addresses, return an error so ClientConn\n\t// will trigger re-resolve. Also records this as a resolver error, so when\n\t// the overall state turns transient failure, the error message will have\n\t// the zero address information.\n\tif len(s.ResolverState.Addresses) == 0 {\n\t\tb.ResolverError(errors.New(\"produced zero addresses\"))\n\t\treturn balancer.ErrBadResolverState\n\t}\n\n\tb.regeneratePicker()\n\tb.cc.UpdateState(balancer.State{ConnectivityState: b.state, Picker: b.picker})\n\treturn nil\n}\n\n// mergeErrors builds an error from the last connection error and the last\n// resolver error.  Must only be called if b.state is TransientFailure.\nfunc (b *baseBalancer) mergeErrors() error {\n\t// connErr must always be non-nil unless there are no SubConns, in which\n\t// case resolverErr must be non-nil.\n\tif b.connErr == nil {\n\t\treturn fmt.Errorf(\"last resolver error: %v\", b.resolverErr)\n\t}\n\tif b.resolverErr == nil {\n\t\treturn fmt.Errorf(\"last connection error: %v\", b.connErr)","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/balancer/base/balancer.go#L120-L156","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","If using a control plane like Istio, ensure the gateway/listener template includes the router filter with its full typed config.","For local testing, construct the LDS HttpFilter with cfg set to an *anypb.Any wrapping an empty pb.Router{}."],"exampleFix":"// before:\nhttpFilters = append(httpFilters, &v3listenerpb.HttpFilter{\n    Name: \"envoy.filters.http.router\",\n    // IsTerminal will not recognize it; typed_config missing\n})\n\n// after:\nrouterAny, _ := anypb.New(&pb.Router{})\nhttpFilters = append(httpFilters, &v3listenerpb.HttpFilter{\n    Name:         \"envoy.filters.http.router\",\n    ConfigType: &v3listenerpb.HttpFilter_TypedConfig{TypedConfig: routerAny},\n})","handlingStrategy":"validation","validationCode":"// Validate every http_filter in an LDS resource has a non-nil typed_config:\nfor _, hf := range listener.GetApiListener().GetHttpFilters() {\n    if hf.GetTypedConfig() == nil && hf.GetIsOptional() == false {\n        if isRouterFilterName(hf.GetName()) {\n            return fmt.Errorf(\"router filter %q has no typed_config\", hf.GetName())\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always populate typed_config for every entry in the http_filters array, even for the terminal router filter.","Use a template or infrastructure-as-code module that enforces typed_config presence.","Validate LDS resources with a schema checker before sending them to the data plane."],"tags":["xds","router","grpc","config","proto"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}