hermes - ✅(Solved) Fix Dashboard TUI WebSocket handshake hangs on FastAPI 0.136 — /api/pty times out [2 pull requests, 1 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#19914Fetched 2026-05-05 06:04:24
View on GitHub
Comments
0
Participants
1
Timeline
7
Reactions
0
Author
Participants
Timeline (top)
labeled ×3cross-referenced ×2referenced ×2

Error Message

  1. Observe browser console error: WebSocket connection to /api/pty failed

Fix Action

Workaround

Running hermes --tui via SSH terminal works fine. The bug is specifically in the WebSocket PTY bridge embedded in the web dashboard.

PR fix notes

PR #19938: fix(tui): accept WebSocket handshake before close to fix FastAPI 0.136 hang on /api/pty

Description (problem / solution / changelog)

Fixes #19914

Root cause: FastAPI / Starlette 0.136 changed WebSocket close() semantics. Calling ws.close() before ws.accept() now hangs because the ASGI websocket.connect message must be consumed (via accept()) before a close frame can be sent. The three dashboard WebSocket handlers (/api/pty, /api/ws, /api/pub) all called ws.close() in early-return paths before ws.accept(), causing the handshake to hang indefinitely on FastAPI 0.136.

Fix: Move ws.accept() to the top of each handler before any early-return close(). The same close codes (4401, 4403, 4400) are returned — just after the handshake completes rather than before.

handle_ws change: Added an already_accepted keyword argument so gateway_ws can pass already_accepted=True after accepting itself, preventing a double-accept error.

Files changed:

  • hermes_cli/web_server.py: /api/pty, /api/ws, /api/pub handlers
  • tui_gateway/ws.py: handle_ws already_accepted parameter

5 new tests in tests/hermes_cli/test_pty_ws_accept_ordering.py, all passing.

Changed files

  • hermes_cli/web_server.py (modified, +20/-8)
  • plugins/kanban/dashboard/plugin_api.py (modified, +5/-3)
  • tests/hermes_cli/conftest.py (added, +55/-0)
  • tests/hermes_cli/test_pty_ws_accept_ordering.py (added, +126/-0)
  • tests/hermes_cli/test_web_server.py (modified, +14/-8)
  • tests/plugins/test_kanban_dashboard_plugin.py (modified, +6/-4)
  • tui_gateway/ws.py (modified, +12/-3)

PR #19943: fix(cli): run dashboard PTY spawn off the event loop

Description (problem / solution / changelog)

Run _resolve_chat_argv and PtyBridge.spawn in a thread executor so the dashboard /api/pty WebSocket handshake doesn't hang on FastAPI 0.136.

What changed and why

  • hermes_cli/web_server.py: /api/pty now invokes _resolve_chat_argv and PtyBridge.spawn via loop.run_in_executor. Both are synchronous and can take seconds (_resolve_chat_argv may shell out to npm install / npm run build; PtyBridge.spawn forks a child). Running them inline starved uvicorn's websockets task so the HTTP 101 upgrade was never flushed and the browser saw the handshake hang. Errors raised from these calls (SystemExit, PtyUnavailableError, FileNotFoundError, OSError) still surface to the client as before.
  • tests/hermes_cli/test_web_server.py: added test_argv_resolve_and_spawn_run_off_event_loop, which monkeypatches _resolve_chat_argv and PtyBridge.spawn to record whether asyncio.get_running_loop() succeeds inside them. With the fix both run on a worker thread; without it they'd run on the loop thread (verified by reverting the fix locally — the new test fails as expected).

How to test

  • pytest tests/hermes_cli/test_web_server.py -q -k Pty
  • Manual: hermes dashboard --tui, open the dashboard's Chat tab, confirm the xterm.js terminal connects without the WebSocket handshake timing out.

What platforms tested on

  • macOS on darwin-arm64 (local) — full tests/hermes_cli/test_web_server.py (138 passed) and the targeted Pty subset (20 passed).

Fixes #19914

<!-- autocontrib:worker-id=issue-new-b47427ee kind=pr-open -->

Changed files

  • hermes_cli/web_server.py (modified, +17/-5)
  • tests/hermes_cli/test_web_server.py (modified, +39/-0)

Code Example

WebSocket connection to ws://host:9119/api/pty?token=... failed
RAW_BUFFERClick to expand / collapse

Bug Description

When running hermes dashboard --tui, the WebSocket connection to /api/pty hangs and times out during the opening handshake. The browser console shows:

WebSocket connection to ws://host:9119/api/pty?token=... failed

The HTTP portion works fine (GET / returns 200, GET /api/ws upgrades successfully), but /api/pty never completes the WebSocket upgrade — the server-side ws.accept() call appears to block indefinitely.

Environment

  • Hermes version: v0.12.0
  • Python: venv at /root/.hermes/hermes-agent/venv/bin/python3
  • FastAPI: 0.136.0
  • OS: Linux (Ubuntu-based)
  • Deployment: standalone (not Docker)

Steps to Reproduce

  1. Run hermes dashboard --tui
  2. Open browser at http://server:9119
  3. Click the Chat tab
  4. Observe browser console error: WebSocket connection to /api/pty failed
  5. The chat area shows [Session ended] — no input possible

Technical Details

  • curl -v -H Upgrade: websocket ... http://127.0.0.1:9119/api/pty — connection is accepted but HTTP 101 never returned — hangs
  • Using websockets Python library to test locally: websockets.connect() to /api/pty — times out after open_timeout
  • /api/ws endpoint works correctly — WebSocket handshake completes fine
  • The issue appears to be in the PTY bridge code path — PtyBridge.spawn() seems to block before ws.accept() is called

Probable Cause

FastAPI 0.136.0 may have a regression where calling ws.accept() inside a WebSocket handler that uses run_in_executor for a synchronous blocking operation (PtyBridge spawn) causes the handshake to hang.

Expected Behavior

The Chat tab in the dashboard should display a working terminal/chat interface, allowing the user to type messages and interact with the agent — same as hermes --tui in the terminal.

Workaround

Running hermes --tui via SSH terminal works fine. The bug is specifically in the WebSocket PTY bridge embedded in the web dashboard.

extent analysis

TL;DR

The issue can be worked around by modifying the WebSocket handler to avoid using run_in_executor for synchronous blocking operations like PtyBridge.spawn().

Guidance

  • Investigate the PtyBridge.spawn() method to understand why it blocks before ws.accept() is called, potentially causing the WebSocket handshake to hang.
  • Consider using an asynchronous approach for PtyBridge.spawn() to avoid blocking the WebSocket handler.
  • Test the WebSocket connection to /api/pty using tools like curl or the websockets Python library to verify the issue is specific to the PTY bridge code path.
  • Review the FastAPI documentation for any known issues or regressions related to using ws.accept() with synchronous blocking operations.

Example

No code example is provided due to the complexity of the issue and the need for further investigation into the PtyBridge.spawn() method.

Notes

The issue appears to be specific to the PTY bridge code path and the use of run_in_executor for synchronous blocking operations. Further investigation is needed to determine the root cause and develop a comprehensive solution.

Recommendation

Apply workaround: Modify the WebSocket handler to avoid using run_in_executor for synchronous blocking operations like PtyBridge.spawn(), or use an asynchronous approach for PtyBridge.spawn() to avoid blocking the WebSocket handler. This is recommended because it directly addresses the probable cause of the issue and may provide a temporary solution until a more comprehensive fix is available.

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

hermes - ✅(Solved) Fix Dashboard TUI WebSocket handshake hangs on FastAPI 0.136 — /api/pty times out [2 pull requests, 1 participants]