hermes - ✅(Solved) Fix [Bug] All API calls timeout on WSL with HTTP proxy — custom httpx transport bypasses proxy env vars in general agent path [3 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#11609Fetched 2026-04-18 05:59:54
View on GitHub
Comments
1
Participants
2
Timeline
9
Reactions
0
Author
Timeline (top)
referenced ×4cross-referenced ×2subscribed ×2commented ×1

After updating past v2026.4.16, every API call fails with APITimeoutError on WSL2 environments using an HTTP(S) proxy (e.g. Clash TUN mode). The root cause is that Hermes injects a custom httpx.HTTPTransport for TCP keepalive, which causes httpx to silently ignore HTTPS_PROXY/HTTP_PROXY environment variables — the agent then attempts direct connections to API endpoints, which fail on WSL where all outbound traffic must go through the proxy.

Root Cause

  1. _create_openai_client() in run_agent.py injects httpx.Client(transport=httpx.HTTPTransport(...)) for TCP keepalive
  2. When httpx receives a custom transport, it does not automatically configure proxy support from environment variables
  3. On WSL, direct connections to public API endpoints are blocked by network architecture — all traffic must route through the Clash TUN interface
  4. The TLS handshake hangs indefinitely because packets are dropped (no route without proxy)
  5. Result: APITimeoutError on every call

This is the same root cause as #11414, which fixed it for the Codex path specifically but left the general agent path (run_agent.py) unpatched.

Fix Action

Workaround

Roll back to v2026.4.16:

cd ~/.hermes/hermes-agent
hermes gateway stop
git checkout v2026.4.16
pip install . --quiet
hermes gateway restart

PR fix notes

PR #11664: fix(agent): respect proxy env in general client path

Description (problem / solution / changelog)

What does this PR do?

Fixes the general agent OpenAI client path so Hermes still honors HTTP_PROXY / HTTPS_PROXY / ALL_PROXY when those environment variables are configured.

The regression reported in #11609 comes from _create_openai_client() always injecting a custom httpx.HTTPTransport for TCP keepalive. Once that custom transport is passed in, httpx stops auto-applying env proxy settings, so proxied WSL setups direct-connect and time out.

This PR keeps the keepalive transport for direct connections, but skips that injection when a proxy env var is present so the SDK/httpx default proxy-aware transport path stays intact.

Related Issue

Fixes #11609

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • add _has_proxy_env_configured() in agent/auxiliary_client.py
  • skip custom keepalive httpx.HTTPTransport injection in run_agent.py::_create_openai_client() when proxy env vars are set
  • add regression coverage for proxy env detection and for the proxied _create_openai_client() path
  • make the _create_openai_client reuse tests hermetic by giving the test agent explicit dummy credentials instead of depending on local machine provider config

How to Test

  1. python3 -m pytest -o addopts='' tests/agent/test_proxy_and_url_validation.py tests/run_agent/test_create_openai_client_reuse.py -q
  2. Set HTTPS_PROXY=http://127.0.0.1:7897 (or another valid local proxy) in a WSL/proxied shell
  3. Run a normal Hermes chat flow and confirm the client path no longer direct-connects through a custom transport that bypasses env proxy handling

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS local targeted regression tests

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Screenshots / Logs

  • python3 -m pytest -o addopts='' tests/agent/test_proxy_and_url_validation.py tests/run_agent/test_create_openai_client_reuse.py -q
  • 20 passed in 30.94s

Changed files

  • agent/auxiliary_client.py (modified, +9/-0)
  • run_agent.py (modified, +6/-2)
  • tests/agent/test_proxy_and_url_validation.py (modified, +23/-1)
  • tests/run_agent/test_create_openai_client_reuse.py (modified, +33/-0)

PR #11733: fix(agent): honor configured proxy in OpenAI client path (#11609)

Description (problem / solution / changelog)

What does this PR do?

Fixes #11609 where HTTPS_PROXY / HTTP_PROXY / macOS SystemConfiguration proxy / Windows registry proxy were silently ignored after #11277 landed. The custom httpx.HTTPTransport injected for TCP keepalives (#10324) made httpx skip env-proxy mount construction, so requests went direct to the destination host regardless of proxy configuration — breaking WSL users, users behind corporate proxies, and users routing outbound traffic through local proxies.

This PR detects any configured proxy via urllib.request.getproxies() — the same function httpx's trust_env=True path uses internally — and skips the keepalive injection in that case, letting the SDK build a default httpx.Client with full trust_env proxy support, including system-level proxy configuration on macOS and Windows.

The proxy path intentionally drops the keepalive injection. Re-implementing httpx's proxy resolution (env + NO_PROXY with IPv6 / CIDR / scheme-qualified entries + macOS SystemConfiguration + Windows registry) by hand proved too fragile; the httpx socket in proxy mode is a loopback/LAN hop to the local proxy process, and httpx's read timeout still fires if that side stalls. Keepalive remains applied on the direct-connect path, so #10324 stays addressed there.

Additionally widens _force_close_tcp_sockets to walk both _transport and _mounts. Once the proxied client path activates, live sockets live under mount pools rather than the default transport, so the existing CLOSE-WAIT cleanup would otherwise silently miss them on every client rebuild.

Related Issue

Fixes #11609

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✅ Tests (adding or improving test coverage)

Changes Made

  • run_agent.py:
    • add AIAgent._has_proxy_configured() — detects configured proxy via urllib.request.getproxies(), matching httpx trust_env semantics
    • gate the keepalive httpx.Client injection on not _has_proxy_configured() so proxied setups delegate to httpx trust_env
    • add AIAgent._iter_client_sockets() helper walking both _transport and _mounts
    • refactor _force_close_tcp_sockets and _cleanup_dead_connections to use the new iterator
  • tests/run_agent/test_create_openai_client_reuse.py:
    • 7 new regression tests, alongside the existing client-reuse guards, covering proxy-env / each-proxy-key / direct-path-keepalive / _has_proxy_configured / system-proxy / mount iteration / mount force-close
    • hermetic proxy stubbing via urllib.request.getproxies to insulate tests from the developer's machine config

How to Test

Scoped tests that exercise the change:

source venv/bin/activate
python -m pytest tests/run_agent/test_create_openai_client_reuse.py -q

Expected: 9 passed (no failures, no skips).

End-to-end reproduction:

  1. Run hermes-agent from a shell with HTTPS_PROXY=http://127.0.0.1:<port> pointing at a local proxy, or with a macOS System Settings → Network proxy configured.
  2. Before this PR: requests may bypass the proxy and direct-connect to the destination host, which can surface as connection failures, timeouts, or unexpected routing behavior depending on the network.
  3. After this PR: lsof -i -nP | grep python shows outbound sockets terminating at the proxy address, not the destination host.

Note: pytest tests/ -q currently reports pre-existing platform/environment failures on upstream main (Discord / Matrix / WSL / file-staleness suites); the scoped regression tests for this change pass cleanly.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes
  • I've tested on my platform: macOS 26.4

Documentation & Housekeeping

  • I've updated relevant documentation — N/A (no user-facing config change)
  • I've updated cli-config.yaml.example — N/A
  • I've updated CONTRIBUTING.md or AGENTS.md — N/A
  • I've considered cross-platform impact — getproxies() transparently reads env vars on all platforms, macOS SystemConfiguration, and Windows registry; TCP_KEEPIDLE / TCP_KEEPALIVE platform branch already in place
  • I've updated tool descriptions/schemas — N/A

Changed files

  • run_agent.py (modified, +96/-51)
  • tests/run_agent/test_create_openai_client_reuse.py (modified, +257/-0)

PR #12010: fix(agent): honor proxy-aware OpenAI client routing (#5454)

Description (problem / solution / changelog)

Summary

  • skip injected keepalive httpx.Client creation when a proxy is configured so the OpenAI SDK/httpx default trust_env path owns routing
  • preserve the existing TCP keepalive transport on the direct-connect path
  • extend socket cleanup/dead-connection probing to walk proxy mount transports as well as the default transport
  • add regression coverage for proxy detection, direct-path keepalive, and mount-based socket cleanup

Problem

run_agent.py::_create_openai_client() currently injects httpx.HTTPTransport(socket_options=...) to mitigate the CLOSE-WAIT hang from #10324.

That transport path has an important side effect: once Hermes hands httpx an explicit transport, httpx no longer constructs its normal proxy mounts from trust_env=True. In practice this means LLM traffic can silently bypass HTTP_PROXY / HTTPS_PROXY / ALL_PROXY or system proxy settings and direct-connect instead.

That matches the behavior reported in #5454 and the later generalized report in #11609.

Why this fix

There were a few prior approaches in related PRs:

  • #11414, #11664, #11943: skip injection when proxy env vars are present
  • #11702: explicitly wire proxy= onto the custom HTTPTransport
  • #11733: detect proxies the same way httpx does and let the SDK/httpx default client own proxy routing, while broadening cleanup for mount-based transports

This PR follows the #11733 direction because it is the most correct salvage:

  • it uses urllib.request.getproxies() so detection matches httpx trust_env behavior more closely than env-only checks
  • it avoids partially reimplementing httpx proxy semantics around NO_PROXY and system proxy sources
  • it fixes the adjacent cleanup gap where proxied clients place live sockets under _mounts, which the old _force_close_tcp_sockets() / _cleanup_dead_connections() logic would miss

Changes

  • run_agent.py
    • add AIAgent._has_proxy_configured() using urllib.request.getproxies()
    • gate keepalive transport injection on not self._has_proxy_configured()
    • add AIAgent._iter_client_sockets() covering both _transport and _mounts
    • refactor _force_close_tcp_sockets() and _cleanup_dead_connections() to reuse that iterator
  • tests/run_agent/test_create_openai_client_reuse.py
    • add proxy/detection regression tests
    • lock the direct-path keepalive invariant
    • add coverage that mount-based sockets are included in cleanup

Verification

  • uv run --extra dev pytest tests/run_agent/test_create_openai_client_reuse.py tests/run_agent/test_create_openai_client_kwargs_isolation.py -q
  • uv run --extra dev pytest tests/agent/test_proxy_and_url_validation.py -q
  • python3 -m py_compile run_agent.py tests/run_agent/test_create_openai_client_reuse.py

Notes

  • This intentionally keeps the proxy path on the SDK/httpx default client rather than preserving the custom keepalive transport there.
  • Direct-connect users still retain the #10324 mitigation.
  • Proxied users regain httpx-native proxy resolution instead of Hermes trying to reproduce it incompletely.

Closes #5454 Related to #10324 Related to #11609

Changed files

  • run_agent.py (modified, +78/-51)
  • tests/run_agent/test_create_openai_client_reuse.py (modified, +205/-0)

Code Example

import os

def _create_openai_client(self, client_kwargs, *, reason, shared):
    has_proxy = any(os.getenv(k) for k in ("HTTPS_PROXY", "HTTP_PROXY", "ALL_PROXY"))
    
    if "http_client" not in client_kwargs:
        if has_proxy:
            # Let httpx auto-detect proxy from env — skip custom transport
            pass  
        else:
            client_kwargs["http_client"] = httpx.Client(
                transport=httpx.HTTPTransport(keepalive_expiry=30)
            )
    
    return OpenAI(**client_kwargs)

---

cd ~/.hermes/hermes-agent
hermes gateway stop
git checkout v2026.4.16
pip install . --quiet
hermes gateway restart
RAW_BUFFERClick to expand / collapse

Summary

After updating past v2026.4.16, every API call fails with APITimeoutError on WSL2 environments using an HTTP(S) proxy (e.g. Clash TUN mode). The root cause is that Hermes injects a custom httpx.HTTPTransport for TCP keepalive, which causes httpx to silently ignore HTTPS_PROXY/HTTP_PROXY environment variables — the agent then attempts direct connections to API endpoints, which fail on WSL where all outbound traffic must go through the proxy.

Environment

  • OS: WSL2 Ubuntu on Windows 11
  • Proxy: Clash TUN mode (127.0.0.1:7897), injected via HTTPS_PROXY/HTTP_PROXY env vars
  • Hermes: v0.10.0 (v2026.4.16) is the last working version; any version after this breaks
  • Python: 3.10
  • Providers affected: all (OpenAI, Anthropic, OpenRouter, etc.)

Steps to Reproduce

  1. Run Hermes on WSL2 with an HTTP(S) proxy configured via environment variables
  2. Update to any version after v2026.4.16
  3. Try any API call (e.g. hermes chat)

Expected Behavior

API calls succeed, routed through the configured proxy.

Actual Behavior

Every API call times out after 3 retry attempts with APITimeoutError.

Root Cause Analysis

  1. _create_openai_client() in run_agent.py injects httpx.Client(transport=httpx.HTTPTransport(...)) for TCP keepalive
  2. When httpx receives a custom transport, it does not automatically configure proxy support from environment variables
  3. On WSL, direct connections to public API endpoints are blocked by network architecture — all traffic must route through the Clash TUN interface
  4. The TLS handshake hangs indefinitely because packets are dropped (no route without proxy)
  5. Result: APITimeoutError on every call

This is the same root cause as #11414, which fixed it for the Codex path specifically but left the general agent path (run_agent.py) unpatched.

Related Issues

  • #11414 — Fixed proxy bypass for Codex clients only
  • #11249 — httpx.Client shared/close bug (same _create_openai_client area)
  • #10324 — CLOSE-WAIT hangs with custom providers
  • #6403 — Malformed proxy env detection

Suggested Fix

Apply the same pattern as #11414 to the general agent path in run_agent.py:

import os

def _create_openai_client(self, client_kwargs, *, reason, shared):
    has_proxy = any(os.getenv(k) for k in ("HTTPS_PROXY", "HTTP_PROXY", "ALL_PROXY"))
    
    if "http_client" not in client_kwargs:
        if has_proxy:
            # Let httpx auto-detect proxy from env — skip custom transport
            pass  
        else:
            client_kwargs["http_client"] = httpx.Client(
                transport=httpx.HTTPTransport(keepalive_expiry=30)
            )
    
    return OpenAI(**client_kwargs)

Workaround

Roll back to v2026.4.16:

cd ~/.hermes/hermes-agent
hermes gateway stop
git checkout v2026.4.16
pip install . --quiet
hermes gateway restart

Why This Matters

WSL2 users who need a proxy for outbound access (common in China, corporate networks, etc.) cannot use any Hermes version after v2026.4.16. The fix is minimal (same approach as #11414) and non-breaking for users without proxies.

extent analysis

TL;DR

Apply the suggested fix to the general agent path in run_agent.py to enable proxy support for HTTP(S) connections.

Guidance

  • Verify that the HTTPS_PROXY and HTTP_PROXY environment variables are set correctly in your WSL2 environment.
  • Check if the issue is resolved by applying the suggested fix to the _create_openai_client function in run_agent.py.
  • If the fix is not feasible, consider rolling back to version v2026.4.16 as a temporary workaround.
  • Ensure that the httpx library is properly configured to use the proxy settings from the environment variables.

Example

The suggested fix involves modifying the _create_openai_client function to conditionally use the custom httpx.HTTPTransport only when no proxy is detected:

def _create_openai_client(self, client_kwargs, *, reason, shared):
    has_proxy = any(os.getenv(k) for k in ("HTTPS_PROXY", "HTTP_PROXY", "ALL_PROXY"))
    
    if "http_client" not in client_kwargs:
        if has_proxy:
            # Let httpx auto-detect proxy from env — skip custom transport
            pass  
        else:
            client_kwargs["http_client"] = httpx.Client(
                transport=httpx.HTTPTransport(keepalive_expiry=30)
            )
    
    return OpenAI(**client_kwargs)

Notes

The suggested fix is based on the same approach used to resolve issue #11414, which fixed the proxy bypass for Codex clients. This fix should be applied to the general agent path in run_agent.py to enable proxy support for all API calls.

Recommendation

Apply the suggested fix to the general agent path in run_agent.py, as it is a minimal and non-breaking change that should resolve the issue for WSL2 users with HTTP(S) proxies.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING