Skip to content

fix(proxy): close upstream session if response.prepare fails - #29279

Open
Jiahui-Gu wants to merge 1 commit into
NousResearch:mainfrom
Jiahui-Gu:fix/proxy-aiohttp-session-leak
Open

fix(proxy): close upstream session if response.prepare fails#29279
Jiahui-Gu wants to merge 1 commit into
NousResearch:mainfrom
Jiahui-Gu:fix/proxy-aiohttp-session-leak

Conversation

@Jiahui-Gu

Copy link
Copy Markdown
Contributor

Summary

hermes_cli/proxy/server.py::_open_upstream (lines 204-244) opened an upstream ClientSession and ClientResponse, then constructed the local web.StreamResponse and called await resp.prepare(request) outside the existing try/finally cleanup block. If the client disconnected mid-prepare (ConnectionResetError, ClientConnectionError, browser cancel, curl --max-time), the exception bypassed cleanup and both the aiohttp ClientSession and ClientResponse leaked. On a busy reverse-proxy deployment this gradually exhausts file descriptors / socket pool.

Fix

Move web.StreamResponse(...) and await resp.prepare(request) inside the try block. The existing finally (upstream_resp.release() + await session.close()) now runs on every exception path.

Test plan

  • tests/hermes_cli/test_proxy.py::test_server_closes_session_when_prepare_fails — subclasses web.StreamResponse to raise ClientConnectionError from prepare(), patches it into the proxy module, and tracks ClientSession.__init__/close + ClientResponse.release. Asserts the proxy's upstream session is closed before runner teardown
  • Verified the test FAILS on the original buggy server.py and passes on the fix
  • Full tests/hermes_cli/test_proxy.py (37 tests) passes

🤖 Generated with Claude Code

web.StreamResponse construction and resp.prepare(request) were outside
the try/finally that closes the upstream aiohttp ClientSession and
ClientResponse. If prepare() raises (e.g. the client disconnects before
headers are flushed) the handler exits via exception and neither
upstream_resp.release() nor session.close() is ever called, leaking
sockets on every interrupted request.

Move StreamResponse construction and prepare() inside the existing try
block so the finally that releases the upstream response and closes the
session runs in all paths.

Add a regression test that subclasses web.StreamResponse to raise
ClientConnectionError from prepare(), tracks ClientSession.__init__/close
and ClientResponse.release, and asserts the proxy-opened session is
closed and the upstream response is released before the test tears
down the runners. Verified to fail on the pre-fix code.

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
@alt-glitch alt-glitch added type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists labels May 20, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused regression fix. Current main still calls await resp.prepare(request) before the cleanup try/finally in hermes_cli/proxy/server.py:220-234, so a prepare failure can bypass upstream_resp.release() and await session.close().

The change in f020767c002d4b18be4e5dc8737b4d3cb25314e6 places response construction and preparation inside that existing cleanup scope, and the added aiohttp test exercises the failure path with a real proxy and upstream runner.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users area/sessions Session lifecycle, resume, persistence, history labels Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants