Skip to content

WS: order_group_updates sequence gap has no recovery path (consumer silently misses events) #205

Closed

Description

@TexasCoding

Summary

SEQUENCED_CHANNELS includes order_group_updates, so gaps ARE detected on that channel — SequenceTracker.track() fires on_gap and returns False so dispatch is skipped. But _handle_seq_gap (in kalshi/ws/client.py) only has a recovery branch for sub.channel == 'orderbook_delta'. For an order_group_updates gap:

  1. The gap-causing message is dropped (correctly — dispatch was skipped).
  2. The seq tracker advances _last_seq[sid] to the new seq (sequence.py:70).
  3. No resubscribe, no state reset, no signal to the consumer.

The consumer iterator silently misses one or more events. For order-group machinery tracking contracts_limit / event_type transitions, this leaves the consumer's view permanently diverged from the server.

This is a sibling of the orderbook-resync issue but on a different channel and with no recovery code path at all (orderbook at least clears local state).

Location

  • kalshi/ws/client.py:393-403 — branch only for orderbook_delta
  • kalshi/ws/sequence.py:68-75 — gap path advances watermark before returning False

Evidence

# kalshi/ws/client.py:393-403
if sub and sub.channel == "orderbook_delta":
    tickers = sub.params.get("market_tickers", [])
    ...
# ← order_group_updates falls off the end; no recovery
# kalshi/ws/sequence.py:68-75
gap = SequenceGap(sid=sid, expected=expected, received=seq)
logger.warning("Sequence gap: ...")
self._last_seq[sid] = seq   # advance, so future gaps won't fire here either

if self._on_gap is not None:
    await self._on_gap(gap)
return False

Recommended fix

Route non-orderbook sequenced channels through a generic resubscribe helper. For order_group_updates:

  1. Unsubscribe and re-subscribe to the affected sid (the server will re-emit the current order-group state).
  2. Until the resubscribe completes, stash incoming frames (the resubscribe-window mechanism from WS: data frames arriving during reconnect+resubscribe are dropped instead of stashed #176 already exists in SubscriptionManager — reuse it).
  3. If resubscribe fails, surface KalshiSequenceGapError through the consumer iterator (see separate "WS error context" issue) with channel='order_group_updates', sid, last_seq, next_seq.

Add a regression test that injects a gap on order_group_updates (e.g., via direct SequenceTracker._last_seq[sid] = 5 then send seq=10) and asserts a recovery action fires.

This issue and the orderbook-resync issue (critical) share the same _handle_seq_gap body — fix them together so the per-channel routing is unified.

Severity & category

medium / correctness, ws

Activity

  1. added
    bugSomething isn't working
    wsWebSocket-related
    on May 21, 2026
  2. added a commit that references this issue on May 21, 2026
    f5db1a1
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