Skip to content

WS foot-gun: callback-only client silently no-ops when no subscribe_* is called #175

Description

@TexasCoding

From #106 F-P-16. Severity: medium UX foot-gun (silent no-op).

Behavior

kalshi/ws/client.py only starts _recv_loop from inside _subscribe_via_lock at line 378 (self._ensure_recv_loop()). If a user:

  1. Constructs KalshiWebSocket(...),
  2. Registers callbacks via ws.on("trade")(handler) / ws.on_error(handler),
  3. Enters async with ws.connect() as session:,
  4. Calls await session.run_forever(),

…without ever calling a subscribe_* method, run_forever() returns immediately (because self._recv_task is None, see kalshi/ws/client.py:529-532). No messages are ever read from the socket. Server-pushed control frames are also silently dropped.

This is the documented callback-mode workflow in README.md. The README does not say a subscribe is also required, and the type system doesn't catch it.

Options

A. Auto-start _recv_loop on connect. Simple, drains server pushes, but no SID mapping exists so data frames still no-op until subscribe. Doesn't fix the actual signal-loss problem, just the silent-return symptom.

B. Raise in run_forever() if no subscription exists. Loud signal at the call site, no silent hang. Could be a KalshiSubscriptionError("run_forever() requires at least one active subscription").

C. Document only. Update README + add a docstring on run_forever calling out the requirement.

Recommendation

B, with the option to soften to a warning later if it proves too strict. Errors at the obvious failure point beat silent no-ops.

Acceptance

  • run_forever() without an active subscription does not silently return; raises KalshiSubscriptionError (or whichever error type matches the WS error hierarchy).
  • Test in tests/ws/test_client.py covering the callback-only-no-subscribe path.
  • README updated to clarify the subscribe-then-callback flow.
  • Sibling foot-gun "run_forever graceful shutdown" tracked in a separate issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    polishCode-quality and DX improvements; non-functionalwsWebSocket-related

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions