Skip to content

ws: KalshiWebSocket._stop() never retrieves a recv-loop exception (asyncio "Task exception was never retrieved") #413

Closed

Description

@TexasCoding

Context

Surfaced by the claude[bot] review of PR #403. The perps PerpsWebSocket._stop() had this exact bug and was fixed on the perps branch (feat/perps-api); the prediction-API KalshiWebSocket._stop() has the identical structure and should be fixed in tandem. Kept out of the perps PR to preserve its scope.

Problem

kalshi/ws/client.py _stop() (lines ~209-217):

if self._recv_task and not self._recv_task.done():
    with contextlib.suppress(asyncio.CancelledError, Exception):
        await asyncio.wait_for(self._recv_task, timeout=2.0)
    if not self._recv_task.done():
        self._recv_task.cancel()
        with contextlib.suppress(asyncio.CancelledError, Exception):
            await self._recv_task

When _recv_loop exits with an unhandled exception (e.g. a permanent-close-code KalshiConnectionError, or the re-raise in the catchall handler), the task is already done by the time _stop() runs. The not self._recv_task.done() guard is False, so the whole block is skipped and the stored exception is never retrieved. asyncio then logs "Task exception was never retrieved" when the task is garbage-collected.

Proposed fix

Add an elif to retrieve the exception when the task is already finished (mirrors the perps fix on feat/perps-api):

elif self._recv_task is not None:
    # Task already finished (e.g. recv loop raised on a permanent close);
    # retrieve the stored exception so asyncio doesn't log
    # "Task exception was never retrieved" when it is GC'd.
    with contextlib.suppress(asyncio.CancelledError, Exception):
        self._recv_task.exception()

Acceptance criteria

  • KalshiWebSocket._stop() retrieves the exception of an already-done recv task.
  • Regression test: a session whose recv loop exited with an exception emits no "Task exception was never retrieved" warning on _stop() (assert via caplog/warnings or a stored-exception check).
  • mypy strict + full suite pass.

Notes

Identical one-line-class fix as the perps PerpsWebSocket._stop() change (commit on feat/perps-api). Low risk; defensive only.

Activity

  1. added
    bugSomething isn't working
    wsWebSocket-related
    on Jun 5, 2026
  2. added a commit that references this issue on Jun 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingwsWebSocket-related

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions