diff --git a/hermes_cli/web_routers/chat_ws.py b/hermes_cli/web_routers/chat_ws.py index f463430ec3..94bf471939 100644 --- a/hermes_cli/web_routers/chat_ws.py +++ b/hermes_cli/web_routers/chat_ws.py @@ -626,13 +626,17 @@ async def pty_ws(ws: WebSocket) -> None: _discard_active_session_file(ws.app, channel, active_session_file) await _pty_fail(ws, exc) return - await _legacy_pump(ws, bridge) - # The 1:1 PTY died with this socket; nothing survives to keep the - # breadcrumb, so drop the marker instead of leaking the entry (#63553). - # A preexisting marker belongs to a live keep-alive PTY on this channel - # — only the marker this handler allocated is ours to drop. - if not marker_preexisting: - _discard_active_session_file(ws.app, channel, active_session_file) + try: + await _legacy_pump(ws, bridge) + finally: + # The 1:1 PTY died with this socket; nothing survives to keep the + # breadcrumb, so drop the marker instead of leaking the entry (#63553). + # A preexisting marker belongs to a live keep-alive PTY on this channel + # — only the marker this handler allocated is ours to drop. In a + # finally: a handler cancelled mid-teardown (client gone, server + # shutdown) must still drop it. + if not marker_preexisting: + _discard_active_session_file(ws.app, channel, active_session_file) return # Keep-alive path: the PTY outlives this socket; reattach by token.