Skip to content

Errors: KalshiSequenceGapError + KalshiSubscriptionError lack channel / sid / seq context #213

Closed

Description

@TexasCoding

Summary

KalshiSequenceGapError and KalshiSubscriptionError lack the programmatic context a caller needs to write a recovery handler.

KalshiSequenceGapError

Bare subclass with only a docstring — no channel, sid, last_seq, next_seq attributes. Per docs/websockets.md:196-198 and docs/errors.md:124-127 the class exists "for callers wiring their own resync handlers" on top of subscribe(channel, ...). A caller catching it gets only str(e) — they can't tell which channel/sid is gapped or how far behind they are without parsing the message string.

Compare to KalshiRateLimitError (exposes .retry_after) — the asymmetry is the gap.

KalshiSubscriptionError

Exposes only error_code and the message string. A caller catching it cannot programmatically tell which channel or subscription failed, which matters when several subscribe_* calls land in parallel.

Location

kalshi/errors.py:76-89

Evidence

# kalshi/errors.py:76-77
class KalshiSequenceGapError(KalshiWebSocketError):
    """Sequence gap detected that could not be resolved via resync."""

# kalshi/errors.py:84-89
class KalshiSubscriptionError(KalshiWebSocketError):
    """Subscribe/unsubscribe request rejected by server."""

    def __init__(self, message: str, error_code: int | None = None) -> None:
        self.error_code = error_code
        super().__init__(message)

Recommended fix

Extend both classes with structured context populated at every raise site.

class KalshiSequenceGapError(KalshiWebSocketError):
    def __init__(
        self,
        message: str,
        *,
        channel: str | None = None,
        sid: int | None = None,
        client_id: int | None = None,
        last_seq: int | None = None,
        next_seq: int | None = None,
    ) -> None:
        self.channel = channel
        self.sid = sid
        self.client_id = client_id
        self.last_seq = last_seq
        self.next_seq = next_seq
        super().__init__(message)


class KalshiSubscriptionError(KalshiWebSocketError):
    def __init__(
        self,
        message: str,
        *,
        error_code: int | None = None,
        channel: str | None = None,
        client_id: int | None = None,
        op: Literal["subscribe", "unsubscribe", "update_subscription"] | None = None,
    ) -> None:
        self.error_code = error_code
        self.channel = channel
        self.client_id = client_id
        self.op = op
        super().__init__(message)

Update every raise site in kalshi/ws/ to populate the new fields. Update docs/errors.md so the "exposed for custom resync" framing is actually usable.

Backward-compatible: positional message and keyword error_code preserved.

Severity & category

medium / dx, ws

Activity

  1. added a commit that references this issue on May 21, 2026
    f64c07f
  2. added 3 commits that reference this issue on May 21, 2026
    f5db1a1
    04e7ac8
    6511c2d
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

    enhancementNew feature or requestwsWebSocket-related

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions