Skip to content

fix #19: [10] WebSocket connections leak when connection closes abnormally - #41

Merged
CuriosityQuantified merged 1 commit into
mainfrom
feat/issue-19-ws-cleanup
Aug 9, 2026
Merged

CuriosityQuantified merged 1 commit into
mainfrom
feat/issue-19-ws-cleanup

Conversation

@CuriosityQuantified

Copy link
Copy Markdown
Owner

Closes #19

Problem

The WebSocket handler in api/routes.py only cleaned up active_connections inside except WebSocketDisconnect:. Any other disconnect cause (network error, RuntimeError, ConnectionResetError, client crash) bypassed the cleanup, leaving stale WebSocket objects in the dict. Over time this causes unbounded growth of active_connections and failed send attempts on dead connections.

Fix

Moved cleanup to a finally block that always executes regardless of how the receive loop exits. Added defensive guards (simulation_id in active_connections and websocket in active_connections[simulation_id]) to handle edge cases where the connection was never successfully appended. Changed except WebSocketDisconnect to except Exception as exc with a logger.debug call so abnormal disconnects remain traceable.

Changes

  • api/routes.pywebsocket_endpoint: finally-block cleanup, debug logging on exception
  • tests/unit/test_ws_cleanup_regression.py — 5 offline unit tests covering clean disconnect, RuntimeError, ConnectionResetError, last-connection deletion, and multi-connection isolation
  • .github/workflows/ci.yml — named CI step "Run WebSocket cleanup regression" added to unit-tests job

Pre-existing issues noted (not in scope for #19)

  • Race condition in broadcast loops (lines 126, 190): iterating active_connections[simulation_id] list at await points where the finally block can mutate it. Pre-existing; marginally widened by this fix. Suggested fix: list(active_connections.get(simulation_id, [])) snapshot before iteration.
  • No payload size limit on receive_text() — pre-existing DoS vector.
  • No auth on the WebSocket endpoint — pre-existing architectural gap.

Test results

  • WebSocket cleanup regression: 5 passed
  • Full unit suite: 160 passed, 1 skipped, 8 deselected
  • npm run build: succeeded

…mally

Move active_connections cleanup from except WebSocketDisconnect into a
finally block so stale connection objects are removed unconditionally
on any disconnect type (clean, network failure, client crash, etc.).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
save-the-world Ready Ready Preview Aug 9, 2026 4:02am
sim-local Ready Ready Preview Aug 9, 2026 4:02am

@CuriosityQuantified
CuriosityQuantified merged commit b8e041a into main Aug 9, 2026
6 checks passed
@CuriosityQuantified
CuriosityQuantified deleted the feat/issue-19-ws-cleanup branch August 9, 2026 04:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[10] WebSocket connections leak when connection closes abnormally

1 participant