NousResearch/hermes-agent

DoH Telegram IP filtering edge case fix

Cluster 14520. Data as of

Nothing open on GitHub. Fix merged in #20175 on 5 May 2026. Check it covers your case.

The short answer

0open issues
0open PRs
3items grouped
Known?
Yes. 3 related items grouped here, 1 issue already closed.
Fix event
A fix was merged to main in PR #20175. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this resolved, deciding PR #20175. That is separate from the fix event.
Still open
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.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #20175 fix(gateway): keep DoH-confirmed Telegram IPs that match system DNS (salvage #17043)

    merged to main

    files: gateway/platforms/telegram_network.py, tests/gateway/test_telegram_network.py

    Show the diff
    • gateway/platforms/telegram_network.py
      @@ -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]
      @@ -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)
       
      @@ -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
      @@ -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):
      @@ -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):

1 PR closed without merging

From the close comments: closed without comment 1

  • #17043 fix(gateway): keep DoH-confirmed Telegram IPs that match system DNS (#14520) · closed without comment
Closed issues · 1
  • Closed#14520Telegram polling fails when DoH returns same IP as system DNS
How this cluster was grouped
  • Link evidence (structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
  • Pairwise link density 0.67: the share of item 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.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Merged PRs
#20175
Closed issues
1
Attempts
1 PR closed without merging
Fix link checked
5 May 2026

For your agent

The same facts, typed.