# NousResearch/hermes-agent · Telegram CLOSE_WAIT fd leak keepalive fix

## Open now

- Nothing open on GitHub.

These issues and PRs address a file-descriptor leak caused by CLOSE_WAIT connections in the Telegram transport, specifically where keepalive limits were not being applied to the fallback or general-pool transport. The work involves identifying the gap and wiring keepalive settings into the affected transports.

## TL;DR

- 4 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 2 PR(s) closed without merging.
- A fix was merged to main in PR #58982. Verify it addresses your case.
- **Recommended:** read PR #58982 (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-58982`. 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.50`: 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

- #58790: bug(telegram): fallback-IP transport bypasses #31599 keepalive fix → general-pool CLOSE_WAIT fd leak

## Prior & competing PRs

- **#58982: fix(telegram): forward keepalive limits into fallback transport (stop CLOSE_WAIT fd leak)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `plugins/platforms/telegram/adapter.py`, `tests/gateway/test_telegram_network.py`
  - fix hunks:
  - `plugins/platforms/telegram/adapter.py`

    ```diff
    @@ -3025,8 +3025,11 @@ def _env_float(name: str, default: float) -> float:
                 def _with_limits(httpx_kwargs: Optional[dict] = None) -> dict:
                     """Merge tuned keepalive limits into httpx client kwargs.
     
    -                A caller-supplied ``limits`` (none today) is left untouched;
    -                otherwise the CLOSE_WAIT-safe limits are injected.
    +                Used by the proxy and direct-DNS branches, where httpx honours
    +                the client-level ``limits`` kwarg. A caller-supplied ``limits``
    +                is left untouched; otherwise the CLOSE_WAIT-safe limits are
    +                injected. The fallback-IP branch does NOT use this helper — see
    +                the ``_transport_kwargs`` note below for why.
                     """
                     kwargs = dict(httpx_kwargs or {})
                     if _pool_limits is not None and "limits" not in kwargs:
    ```

    ```diff
    @@ -3054,17 +3057,31 @@ def _with_limits(httpx_kwargs: Optional[dict] = None) -> dict:
                     )
                     # Keep request/update pools separate to reduce contention during
                     # polling reconnect + bot API bootstrap/delete_webhook calls.
    +                # httpx ignores the client-level `limits` kwarg when a custom
    +                # `transport` is supplied (#58790).  Unlike the proxy/direct
    +                # branches (which inject limits at the client level via
    +                # `_with_limits`), this branch MUST pass the tuned limits
    +                # directly into TelegramFallbackTransport so its inner
    +                # AsyncHTTPTransport instances honour keepalive_expiry — do not
    +                # route this through `_with_limits`, httpx would discard it.
    +                _transport_kwargs: dict = {}
    +                if _pool_limits is not None:
    +                    _transport_kwargs["limits"] = _pool_limits
                     request = HTTPXRequest(
                         **request_kwargs,
    -                    httpx_kwargs=_with_limits(
    -                        {"transport": TelegramFallbackTransport(fallback_ips)}
    -                    ),
    +                    httpx_kwargs={
    +                        "transport": TelegramFallbackTransport(
    +                            fallback_ips, **_transport_kwargs
    +                        )
    +                    },
                     )
                     get_updates_request = HTTPXRequest(
                         **request_kwargs,
    -                    httpx_kwargs=_with_limits(
    -                        {"transport": TelegramFallbackTransport(fallback_ips)}
    -                    ),
    +                    httpx_kwargs={
    +                        "transport": TelegramFallbackTransport(
    +                            fallback_ips, **_transport_kwargs
    +                        )
    +                    },
                     )
                 elif proxy_url:
                     logger.info("[%s] Proxy detected; passing explicitly to HTTPXRequest: %s", self.name, proxy_url)
    ```
  - `tests/gateway/test_telegram_network.py`

    ```diff
    @@ -354,6 +354,37 @@ def factory(**kwargs):
             assert len(seen_kwargs) == 2
             assert all("proxy" not in kwargs for kwargs in seen_kwargs)
     
    +    def test_forwards_limits_to_inner_transports(self, monkeypatch):
    +        """Verify that caller-supplied limits reach the inner
    +        AsyncHTTPTransport instances (#58790).  httpx ignores the
    +        client-level limits kwarg when a custom transport is
    +        supplied, so the limits must be forwarded via transport_kwargs.
    +        """
    +        seen_kwargs = []
    +
    +        def factory(**kwargs):
    +            seen_kwargs.append(kwargs.copy())
    +            return FakeTransport([], {})
    +
    +        for key in ("HTTPS_PROXY", "HTTP_PROXY", "ALL_PROXY", "https_proxy", "http_proxy", "all_proxy", "TELEGRAM_PROXY", "NO_PROXY", "no_proxy"):
    +            monkeypatch.delenv(key, raising=False)
    +        monkeypatch.setattr(tnet.httpx, "AsyncHTTPTransport", factory)
    +
    +        custom_limits = httpx.Limits(
    +            max_connections=42,
    +            max_keepalive_connections=10,
    +            keepalive_expiry=30.0,
    +        )
    +        transport = tnet.TelegramFallbackTransport(
    +            ["149.154.167.220"], limits=custom_limits
    +        )
    +
    +        # 1 primary + 1 fallback = 2 AsyncHTTPTransport instances
    +        assert len(seen_kwargs) == 2
    +        for kw in seen_kwargs:
    +            assert "limits" in kw
    +            assert kw["limits"] is custom_limits
    +
     
     class TestFallbackTransportClose:
         @pytest.mark.asyncio
    ```
- **#58803: fix(telegram): forward keepalive limits into fallback transport**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `01ee312de`, `#58982`
- **#58804: fix(telegram): default fallback transport to platform keepalive limits (stop CLOSE_WAIT fd leak)**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#58803`, `#58982`

## Fix status

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