{"record":{"id":"8ebb502d998106e0","repo":"grpc/grpc-go","slug":"endpoints-list-is-empty","errorCode":null,"errorMessage":"endpoints list is empty","messagePattern":"endpoints list is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"resolver/resolver.go","lineNumber":350,"sourceCode":"type AuthorityOverrider interface {\n\t// OverrideAuthority returns the authority to use for a ClientConn with the\n\t// given target. The implementation must generate it without blocking,\n\t// typically in line, and must keep it unchanged.\n\t//\n\t// The returned string must be a valid \":authority\" header value, i.e. be\n\t// encoded according to\n\t// [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986#section-3.2) as\n\t// necessary.\n\tOverrideAuthority(Target) string\n}\n\n// ValidateEndpoints validates endpoints from a petiole policy's perspective.\n// Petiole policies should call this before calling into their children. See\n// [gRPC A61](https://github.com/grpc/proposal/blob/master/A61-IPv4-IPv6-dualstack-backends.md)\n// for details.\nfunc ValidateEndpoints(endpoints []Endpoint) error {\n\tif len(endpoints) == 0 {\n\t\treturn errors.New(\"endpoints list is empty\")\n\t}\n\n\tfor _, endpoint := range endpoints {\n\t\tfor range endpoint.Addresses {\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn errors.New(\"endpoints list contains no addresses\")\n}\n","sourceCodeStart":332,"sourceCodeEnd":360,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/resolver/resolver.go#L332-L360","documentation":"Returned by resolver.ValidateEndpoints (resolver/resolver.go:350) when the provided endpoints slice has length zero. Petiole (dual-stack) load-balancing policies call this on their children's results; an empty list means there is nothing to route to. The function is part of the gRPC A61 dual-stack endpoints proposal.","triggerScenarios":"A custom resolver or balancer returns an empty []resolver.Endpoint slice; a DNS or xDS resolution yields no endpoints; calling ValidateEndpoints directly with a zero-length slice.","commonSituations":"Service discovery returns no instances (scaled to zero, misconfigured DNS); a filtering layer strips all endpoints; an upstream control plane pushing an empty resource update.","solutions":["Investigate the resolver/registry to ensure at least one endpoint is being returned.","Return TRANSIENT_FAILURE or keep the previous good resolver state rather than pushing an empty list.","Add a guard in your resolver to log and skip empty updates instead of forwarding them.","Verify the target URI and service name resolve to live backends."],"exampleFix":"// before\ncc.UpdateState(resolver.State{Addresses: nil, Endpoints: nil})\n\n// after\nif len(eps) == 0 {\n    logger.Warn(\"no endpoints; skipping update\")\n    return\n}\nif err := resolver.ValidateEndpoints(eps); err != nil {\n    return err\n}\ncc.UpdateState(resolver.State{Endpoints: eps})","handlingStrategy":"validation","validationCode":"if len(endpoints) == 0 {\n    return fmt.Errorf(\"resolver produced no endpoints\")\n}\nif err := resolver.ValidateEndpoints(endpoints); err != nil {\n    return err\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never push an empty endpoints list to UpdateState; keep the previous state instead.","Log and investigate when discovery returns zero endpoints.","Wrap ValidateEndpoints around all resolver outputs in custom balancers."],"tags":["go","grpc","resolver","load-balancing","service-discovery"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}