# NousResearch/hermes-agent · DoH Telegram IP filtering edge case fix

## Open now

- Nothing open on GitHub.

These issues and PRs address a bug where Telegram polling fails when DNS-over-HTTPS resolves to the same IP address as the system DNS. The fixes aim to preserve DoH-confirmed IPs in this matching scenario rather than discarding them.

## TL;DR

- 3 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #20175. Verify it addresses your case.
- **Recommended:** read PR #20175 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-20175`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.67`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #14520: Telegram polling fails when DoH returns same IP as system DNS

## Prior & competing PRs

- **#20175: fix(gateway): keep DoH-confirmed Telegram IPs that match system DNS (salvage #17043)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `gateway/platforms/telegram_network.py`, `tests/gateway/test_telegram_network.py`
  - fix hunks:
  - `gateway/platforms/telegram_network.py`

    ```diff
    @@ -185,10 +185,13 @@ async def _query_doh_provider(
     async def discover_fallback_ips() -> list[str]:
         """Auto-discover Telegram API IPs via DNS-over-HTTPS.
     
    -    Resolves api.telegram.org through Google and Cloudflare DoH, collects all
    -    unique IPs, and excludes the system-DNS-resolved IP (which is presumably
    -    unreachable on this network).  Falls back to a hardcoded seed list when DoH
    -    is also unavailable.
    +    Resolves api.telegram.org through Google and Cloudflare DoH and returns all
    +    unique A records.  IPs that match the local system resolver are kept rather
    +    than excluded: in many networks the system-DNS IP is the most reliable path
    +    to api.telegram.org and a transient primary-path failure should be retried
    +    against the same address via the IP-rewrite path before the seed list is
    +    consulted (#14520).  Falls back to a hardcoded seed list only when DoH
    +    yields no usable answers.
         """
         async with httpx.AsyncClient(timeout=httpx.Timeout(_DOH_TIMEOUT)) as client:
             doh_tasks = [_query_doh_provider(client, p) for p in _DOH_PROVIDERS]
    ```

    ```diff
    @@ -203,11 +206,11 @@ async def discover_fallback_ips() -> list[str]:
             if isinstance(r, list):
                 doh_ips.extend(r)
     
    -    # Deduplicate preserving order, exclude system-DNS IPs
    +    # Deduplicate preserving order
         seen: set[str] = set()
         candidates: list[str] = []
         for ip in doh_ips:
    -        if ip not in seen and ip not in system_ips:
    +        if ip not in seen:
                 seen.add(ip)
                 candidates.append(ip)
     
    ```

    ```diff
    @@ -219,7 +222,7 @@ async def discover_fallback_ips() -> list[str]:
             return validated
     
         logger.info(
    -        "DoH discovery yielded no new IPs (system DNS: %s); using seed fallback IPs %s",
    +        "DoH discovery yielded no usable IPs (system DNS: %s); using seed fallback IPs %s",
             ", ".join(system_ips) or "unknown",
             ", ".join(_SEED_FALLBACK_IPS),
         )
    ```
  - `tests/gateway/test_telegram_network.py`

    ```diff
    @@ -534,15 +534,20 @@ async def test_google_and_cloudflare_ips_collected(self, monkeypatch):
             assert "149.154.167.221" in ips
     
         @pytest.mark.asyncio
    -    async def test_system_dns_ip_excluded(self, monkeypatch):
    -        """The IP from system DNS is the one that doesn't work — exclude it."""
    +    async def test_system_dns_ip_kept_when_doh_confirms(self, monkeypatch):
    +        """DoH-confirmed IPs are kept even when they match system DNS (#14520).
    +
    +        The system-DNS IP is often the most reliable path; including it as a
    +        fallback lets the IP-rewrite retry recover from transient primary-path
    +        failures instead of jumping straight to the hardcoded seed list.
    +        """
             self._patch_doh(monkeypatch, {
                 "https://dns.google": (200, _doh_answer("149.154.166.110", "149.154.167.220")),
                 "https://cloudflare-dns.com": (200, _doh_answer("149.154.166.110")),
             }, system_dns_ips=["149.154.166.110"])
     
             ips = await tnet.discover_fallback_ips()
    -        assert ips == ["149.154.167.220"]
    +        assert ips == ["149.154.166.110", "149.154.167.220"]
     
         @pytest.mark.asyncio
         async def test_doh_results_deduplicated(self, monkeypatch):
    ```

    ```diff
    @@ -607,15 +612,21 @@ async def test_system_dns_failure_keeps_all_doh_ips(self, monkeypatch):
             assert "149.154.167.220" in ips
     
         @pytest.mark.asyncio
    -    async def test_all_doh_ips_same_as_system_dns_uses_seed(self, monkeypatch):
    -        """DoH returns only the same blocked IP — seed list is the fallback."""
    +    async def test_all_doh_ips_same_as_system_dns_kept(self, monkeypatch):
    +        """DoH agrees with system DNS — keep that IP instead of seed list (#14520).
    +
    +        Previous behavior fell through to ``_SEED_FALLBACK_IPS`` here, but the
    +        seed addresses are not routable on every network.  When DoH confirms
    +        the system IP, that IP is the best candidate we have and should be
    +        used as the fallback target.
    +        """
             self._patch_doh(monkeypatch, {
                 "https://dns.google": (200, _doh_answer("149.154.166.110")),
                 "https://cloudflare-dns.com": (200, _doh_answer("149.154.166.110")),
             }, system_dns_ips=["149.154.166.110"])
     
             ips = await tnet.discover_fallback_ips()
    -        assert ips == tnet._SEED_FALLBACK_IPS
    +        assert ips == ["149.154.166.110"]
     
         @pytest.mark.asyncio
         async def test_cloudflare_gets_accept_header(self, monkeypatch):
    ```
- **#17043: fix(gateway): keep DoH-confirmed Telegram IPs that match system DNS (#14520)**. closed without merge
  - maintainer direction: category `silent_close`; implication `active`

## Fix status

- Merged to main in PR #20175 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-05-05`.
