Summary
Subscription.to_subscribe_params() only forwards keys listed in the hardcoded _SUBSCRIBE_FORWARD_KEYS tuple. Any other key the user passes via the public KalshiWebSocket.subscribe(channel, params={...}) escape hatch is silently dropped before the wire command is sent.
Failure modes:
params={'tickers': [...]} (plural in some channels' docs) is dropped — the server subscribes the consumer to a much broader stream than intended (all markets, all symbols).
params={'send_snapshot': True} (typo of send_initial_snapshot) is dropped — the consumer never gets a snapshot and has to wait for the first delta.
- Any future server-added parameter is invisible to existing SDK clients with no warning.
This is the documented escape hatch for the two channels (control_frames, root) that don't have typed helpers, so users WILL hit it.
Location
kalshi/ws/channels.py:21-30, 48-54
Evidence
# kalshi/ws/channels.py:21-30
_SUBSCRIBE_FORWARD_KEYS = (
"market_ticker",
"market_tickers",
"market_id",
"market_ids",
"shard_factor",
"shard_key",
"send_initial_snapshot",
"skip_ticker_ack",
)
# kalshi/ws/channels.py:48-54
def to_subscribe_params(self) -> dict[str, Any]:
"""Build the params dict for the subscribe command."""
result: dict[str, Any] = {"channels": [self.channel]}
for key in _SUBSCRIBE_FORWARD_KEYS:
if key in self.params:
result[key] = self.params[key]
return result # ← unknown keys silently dropped
Recommended fix
Validate params against the channel's known parameter set at subscribe() time. Either:
Option A (minimal): Raise KalshiSubscriptionError from Subscription.__init__ (or to_subscribe_params) on unknown keys.
Option B (best): Define a per-channel dict[channel, frozenset[str]] of accepted params (with set semantics for the typed helpers + the public escape hatch), and raise on any key not in the channel's set. Drives off the AsyncAPI spec so it stays accurate.
Option C (highest fidelity): Add a typed SubscribeParams Pydantic model per channel with extra='forbid' to fail fast on typos — matches the existing request-body pattern.
Regression test: test_generic_subscribe_rejects_unknown_param_keys invokes ws.subscribe('orderbook_delta', params={'tickerz': ['X']}) and asserts KalshiSubscriptionError.
Severity & category
high / correctness, dx, ws
Summary
Subscription.to_subscribe_params()only forwards keys listed in the hardcoded_SUBSCRIBE_FORWARD_KEYStuple. Any other key the user passes via the publicKalshiWebSocket.subscribe(channel, params={...})escape hatch is silently dropped before the wire command is sent.Failure modes:
params={'tickers': [...]}(plural in some channels' docs) is dropped — the server subscribes the consumer to a much broader stream than intended (all markets, all symbols).params={'send_snapshot': True}(typo ofsend_initial_snapshot) is dropped — the consumer never gets a snapshot and has to wait for the first delta.This is the documented escape hatch for the two channels (
control_frames,root) that don't have typed helpers, so users WILL hit it.Location
kalshi/ws/channels.py:21-30, 48-54Evidence
Recommended fix
Validate
paramsagainst the channel's known parameter set atsubscribe()time. Either:Option A (minimal): Raise
KalshiSubscriptionErrorfromSubscription.__init__(orto_subscribe_params) on unknown keys.Option B (best): Define a per-channel
dict[channel, frozenset[str]]of accepted params (withsetsemantics for the typed helpers + the public escape hatch), and raise on any key not in the channel's set. Drives off the AsyncAPI spec so it stays accurate.Option C (highest fidelity): Add a typed
SubscribeParamsPydantic model per channel withextra='forbid'to fail fast on typos — matches the existing request-body pattern.Regression test:
test_generic_subscribe_rejects_unknown_param_keysinvokesws.subscribe('orderbook_delta', params={'tickerz': ['X']})and assertsKalshiSubscriptionError.Severity & category
high / correctness, dx, ws