Skip to content

WS: resubscribe_one stash is never drained — frames captured during gap recovery are dropped #254

Closed

Description

@TexasCoding

Summary

On every gap recovery (_handle_seq_gap -> resubscribe_one), frames captured between unsubscribe-ack and subscribe-ack sit in self._stash until the next full reconnect (where resubscribe_all clears them). Bounded by stash_maxlen so not unbounded, but accumulates per gap and the captured frames — which legitimately arrived for the new sid window — are never replayed through _process_frame. Post-resubscribe deltas with seq>1 can hit the seq tracker before the snapshot they describe.

Location

  • kalshi/ws/channels.py:452-535 — resubscribe_one stashes but never drains
  • kalshi/ws/client.py:394-431 — _drain_resubscribe_stash is only called from _handle_reconnect

Evidence

# channels.py resubscribe_one:
prev_stashing = self._stashing
self._stashing = True
try:
    ...  # unsubscribe + subscribe via _wait_for_response (stashes by sid)
finally:
    self._stashing = prev_stashing
# No take_stash() / drain anywhere along this path.

take_stash() is only called from client.py:411 _drain_resubscribe_stash, which is only called from _handle_reconnect (full reconnect), not from _handle_seq_gap -> resubscribe_one.

Recommended fix

After resubscribe_one returns successfully, drain its stash through _process_frame (same shape as _drain_resubscribe_stash) — filter by new_sid only, drop the rest with a debug log. Add regression test that injects a delta during a resubscribe window and asserts it is replayed.

Severity & category

medium / reliability, ws

Activity

  1. added
    bugSomething isn't working
    wsWebSocket-related
    on May 21, 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