MoonshotAI / MoonshotAI/kimi-cli

Bug: Web UI "Connecting to session..." infinite spinner when switching sessions

Open
#2,573 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
11.4k
Forks
1.3k
Avg merge
9h 47m
Merged PRs (30d)
2

Description

Version: kimi-cli 1.48.0 (Homebrew, macOS 26.4, arm64)
Component: kimi web — Web UI (Technical Preview)
Browser: Chrome 150.0.7871.187

Description

When switching between sessions in the Web UI (http://127.0.0.1:5494), the UI shows an infinite "Connecting to session..." spinner. The session never connects. This happens consistently on every session switch — not just occasionally.

Restarting the kimi web server temporarily resolves the issue, but it returns after the first session switch.

Steps to Reproduce

  1. Start kimi web
  2. Open the web UI in Chrome
  3. Have 2+ existing sessions
  4. Click on a different session in the sidebar
  5. Result: "Connecting to session..." spinner appears and never resolves
  6. All subsequent session switches also fail

Observed Behavior

  • lsof -i :5494 shows CLOSE_WAIT TCP connections accumulating — the server doesn't properly close WebSocket connections after the client disconnects
  • The healthcheck endpoint (/healthz) responds {"status":"ok"} — the server process itself is alive
  • Only 1 active ESTABLISHED connection exists while others are stuck in CLOSE_WAIT

Root Cause Analysis

Traced through the source code (sessions.py, process.py, frontend JS):

1. end_replay() livelock under message throughput

end_replay() in process.py loops acquiring _ws_lock, copying buffered messages, and sending them. If _read_loop is actively broadcasting, the buffer refills between iterations, causing the loop to spin indefinitely. This blocks send_status_snapshot() from ever executing.

2. Frontend 15-second timeout → infinite reconnect loop

The frontend waits up to 15 seconds for a session_status message after history_complete. If send_status_snapshot() is blocked (by issue #1), the timeout fires, triggering reconnect(), which starts the cycle over: disconnect → 50ms delay → connect → replay → timeout → reconnect...

3. _broadcast() blocks on dead WebSockets

_broadcast() iterates all websockets in _websockets set and calls await ws.send_text() sequentially. A dead/closing WebSocket blocks delivery to all other connected clients.

4. remove_websocket() doesn't call ws.close()

When a WebSocket is removed from the set, the TCP connection isn't explicitly closed. The transport only closes when the handler function returns, but the handler may be stuck in receive_text().

Expected Behavior

Session switching should work seamlessly — disconnect from current session's WebSocket, connect to new session's WebSocket, replay history, and show the session.

Workaround

Restart the web server:

kill $(lsof -ti :5494); sleep 2; kimi web --no-open &

Related

  • PR #1669: "Improve WebSocket reconnection experience in Web UI" — addresses flickering but not the core CLOSE_WAIT / livelock issue

Environment

  • macOS 26.4 (Apple Silicon)
  • kimi-cli 1.48.0 (Homebrew)
  • Chrome 150.0.7871.187
  • Multiple sessions (5-6 workers active)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with sessions.py, process.py, and the frontend JavaScript, tracing end_replay(), _broadcast(), and remove_websocket(). Reproduce switching among multiple sessions while watching the WebSocket connections, then verify that replay completes, session_status arrives, switching continues to work, and CLOSE_WAIT connections do not accumulate.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, python
Domain
backend, cli, frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.