Skip to content

fix(server): WebSocket broadcast blocks on one slow client; malformed frames kill connection without close/log #1154

Description

@Kohei-Wada

Problem

Two robustness gaps in the WebSocket layer:

1. Sequential broadcast with no timeoutpackages/taskdog-server/src/taskdog_server/websocket/connection_manager.py:66-68:

for client_id, connection in list(self.active_connections.items()):
    try:
        await connection.send_json(message)

One slow or wedged peer (TCP window full, suspended terminal, laptop asleep) blocks the entire broadcast loop indefinitely; all other clients stop receiving events. With ~5 concurrent agent clients this is easy to hit.

2. Malformed frames kill the connection without close or logpackages/taskdog-server/src/taskdog_server/api/routers/websocket.py:80-91: receive_json raises on any non-JSON text frame; the bare except Exception removes the client from the manager but never calls websocket.close() and logs nothing. The peer sees a silently dead socket with no close frame to trigger its reconnect logic.

Direction

  • Fan out with asyncio.gather(..., return_exceptions=True), wrap each send in asyncio.wait_for(...), disconnect on timeout.
  • Use receive_text() + guarded json.loads; ignore malformed frames; await websocket.close(...) + logger.exception in the catch-all.

Found during repo-wide audit, 2026-07-25.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority: mediumMedium priority issueservertaskdog-server package

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions