You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
SubscriptionManager::subscribe / subscribe_raw silently fail for every RTDS topic whose
filters are not special-cased by the SDK's manual Serialize impl. For non-chainlink topics
(e.g. crypto_prices_twap_sixty), filters is serialized as a JSON object instead of a compact JSON string. The RTDS service rejects the whole subscribe frame with {"message": "Invalid request body"}, and that reply is dropped by the SDK's message parser,
so callers see a "healthy" connection with zero updates and no error.
This makes crypto_prices_twap_sixty / crypto_prices_twap_thirty unusable through the 0.7.0
SDK and — because nothing surfaces the rejection — very hard to diagnose.
src/rtds/types/request.rs, impl Serialize for Subscription:
ifself.topic == "crypto_prices_chainlink"{// Chainlink: emit filters as string, e.g. "{\"symbol\":\"btc/usd\"}"
map.serialize_entry("filters", filters)?;}elseifletOk(json_value) = serde_json::from_str::<Value>(filters){// Other topics: parse and emit as raw JSON, e.g. ["btcusdt","ethusdt"]
map.serialize_entry("filters",&json_value)?;}
{"symbol":"btc/usd"} is valid JSON, so for crypto_prices_twap_sixty it is emitted as a raw
object:
The RTDS docs require the compact JSON string form
(filters must use the exact compact JSON form shown above, with one lowercase symbol and no spaces, such as {"symbol":"btc/usd"}), i.e.:
Minimal reproduction (no SDK, Node 22+ has a global WebSocket)
constcases=[{name: "filters as compact JSON string",filters: JSON.stringify({symbol: "btc/usd"})},{name: "filters as JSON object (what 0.7.0 emits)",filters: {symbol: "btc/usd"}},];for(constcofcases){constws=newWebSocket("wss://ws-live-data.polymarket.com");ws.onopen=()=>ws.send(JSON.stringify({action: "subscribe",subscriptions: [{topic: "crypto_prices_twap_sixty",type: "update",filters: c.filters}],}));letn=0;ws.onmessage=(ev)=>{n++;if(n===1)console.log(c.name,"first frame:",String(ev.data).slice(0,120));};setTimeout(()=>{console.log(c.name,"frames in 15s:",n);ws.close();},15000);}
Observed output against production RTDS:
filters as compact JSON string first frame: {"connection_id":"…","payload":{"full_accuracy_value":"…","symbol":"btc/usd","timestamp":…,"value":"79765.74…","window_s":60},…}
filters as compact JSON string frames in 15s: 14
filters as JSON object (what 0.7.0 emits) first frame: {"message": "Invalid request body", "connectionId":"…", "requestId":"…"}
filters as JSON object (what 0.7.0 emits) frames in 15s: 1
As a control, crypto_prices_chainlink (string form via the SDK special case) streams normally.
Impact
crypto_prices_twap_sixty / crypto_prices_twap_thirty cannot be consumed at all through subscribe_raw in 0.7.0, and the failure is silent: the socket stays ESTABLISHED, the
application-level PING/PONG heartbeat keeps working, and no error ever reaches the caller.
Serialize filters as the compact JSON string for all RTDS topics (the object/array form
only makes sense for crypto_prices, which expects a JSON array — that one can keep the
special case), or gate the object form on topic == "crypto_prices" only.
Consider surfacing server-level Invalid request body replies to subscribers (or at least
logging them) so subscription rejections are not fully silent.
Related: #90 (SubscriptionManager refcount identity ignores filters — same area of the
subscription path, different failure mode).
Summary
SubscriptionManager::subscribe/subscribe_rawsilently fail for every RTDS topic whosefilters are not special-cased by the SDK's manual
Serializeimpl. For non-chainlink topics(e.g.
crypto_prices_twap_sixty),filtersis serialized as a JSON object instead of acompact JSON string. The RTDS service rejects the whole subscribe frame with
{"message": "Invalid request body"}, and that reply is dropped by the SDK's message parser,so callers see a "healthy" connection with zero updates and no error.
This makes
crypto_prices_twap_sixty/crypto_prices_twap_thirtyunusable through the 0.7.0SDK and — because nothing surfaces the rejection — very hard to diagnose.
Environment
polymarket_client_sdk_v20.7.0 (crates.io),subscribe_rawpathwss://ws-live-data.polymarket.comRoot cause
src/rtds/types/request.rs,impl Serialize for Subscription:{"symbol":"btc/usd"}is valid JSON, so forcrypto_prices_twap_sixtyit is emitted as a rawobject:
{"action":"subscribe","subscriptions":[{"topic":"crypto_prices_twap_sixty","type":"update","filters":{"symbol":"btc/usd"}}]}The RTDS docs require the compact JSON string form
(
filters must use the exact compact JSON form shown above, with one lowercase symbol and no spaces, such as {"symbol":"btc/usd"}), i.e.:{"action":"subscribe","subscriptions":[{"topic":"crypto_prices_twap_sixty","type":"update","filters":"{\"symbol\":\"btc/usd\"}"}]}Minimal reproduction (no SDK, Node 22+ has a global WebSocket)
Observed output against production RTDS:
As a control,
crypto_prices_chainlink(string form via the SDK special case) streams normally.Impact
crypto_prices_twap_sixty/crypto_prices_twap_thirtycannot be consumed at all throughsubscribe_rawin 0.7.0, and the failure is silent: the socket stays ESTABLISHED, theapplication-level PING/PONG heartbeat keeps working, and no error ever reaches the caller.
Suggested fix
filtersas the compact JSON string for all RTDS topics (the object/array formonly makes sense for
crypto_prices, which expects a JSON array — that one can keep thespecial case), or gate the object form on
topic == "crypto_prices"only.Invalid request bodyreplies to subscribers (or at leastlogging them) so subscription rejections are not fully silent.
Related: #90 (SubscriptionManager refcount identity ignores
filters— same area of thesubscription path, different failure mode).