Drift
TypeScript's subscribe() computes firstArg once via nullish-coalescing (args[0] ?? "") and reuses it for both the subscription-reuse dedup key (subKey) and the symbols metadata. Python's subscribe() independently recomputes the dedup key from a different truthiness check — args[0] if args else "", i.e. "is the whole args list non-empty," not "is args[0] itself present/truthy." For args = [None], this produces a different subKey/sub_key between the two SDKs, which changes whether a call is treated as reusing an existing subscription or opening a new one. This is distinct from #1498, which is scoped to the symbols metadata field derivation and does not address the dedup key computation or its consequence for subscription reuse.
TypeScript SDK
sdks/typescript/pmxt/ws-client.ts:348-351:
const firstArg = args[0] ?? "";
const subKey = Array.isArray(firstArg)
? `${method}:${[...firstArg].sort().join(",")}`
: `${method}:${firstArg}`;
For args = [null]: args[0] ?? "" evaluates null ?? "" → firstArg = "", so subKey = "method:".
Python SDK
sdks/python/pmxt/ws_client.py:305-309:
first_arg = args[0] if args else ""
if isinstance(first_arg, list):
sub_key = f"{method}:{','.join(sorted(first_arg))}"
else:
sub_key = f"{method}:{first_arg}"
For args = [None]: args is a non-empty list (truthy), so first_arg = args[0] = None (the if args else "" only checks the list itself, not the element) → sub_key = f"{method}:{first_arg}" = "method:None".
Expected
The same args input should produce the same dedup key in both SDKs, so that whether a given subscribe() call reuses an active subscription or starts a new one is consistent across languages. Both should base the fallback on args[0]'s own truthiness (matching TypeScript's ??), not on whether args as a whole is non-empty.
Impact
For a call shaped like subscribe(exchange, method, [None], ...), TypeScript computes dedup key "method:" while Python computes "method:None" — a different key means the two SDKs disagree on whether a second such call reuses the first subscription (returning cached/pending data) or opens a fresh one, changing observable subscribe-reuse behavior, not just metadata shape.
Found by automated SDK cross-language drift audit
Drift
TypeScript's
subscribe()computesfirstArgonce via nullish-coalescing (args[0] ?? "") and reuses it for both the subscription-reuse dedup key (subKey) and thesymbolsmetadata. Python'ssubscribe()independently recomputes the dedup key from a different truthiness check —args[0] if args else "", i.e. "is the wholeargslist non-empty," not "isargs[0]itself present/truthy." Forargs = [None], this produces a differentsubKey/sub_keybetween the two SDKs, which changes whether a call is treated as reusing an existing subscription or opening a new one. This is distinct from #1498, which is scoped to thesymbolsmetadata field derivation and does not address the dedup key computation or its consequence for subscription reuse.TypeScript SDK
sdks/typescript/pmxt/ws-client.ts:348-351:For
args = [null]:args[0] ?? ""evaluatesnull ?? ""→firstArg = "", sosubKey = "method:".Python SDK
sdks/python/pmxt/ws_client.py:305-309:For
args = [None]:argsis a non-empty list (truthy), sofirst_arg = args[0]=None(theif args else ""only checks the list itself, not the element) →sub_key = f"{method}:{first_arg}"="method:None".Expected
The same
argsinput should produce the same dedup key in both SDKs, so that whether a givensubscribe()call reuses an active subscription or starts a new one is consistent across languages. Both should base the fallback onargs[0]'s own truthiness (matching TypeScript's??), not on whetherargsas a whole is non-empty.Impact
For a call shaped like
subscribe(exchange, method, [None], ...), TypeScript computes dedup key"method:"while Python computes"method:None"— a different key means the two SDKs disagree on whether a second such call reuses the first subscription (returning cached/pending data) or opens a fresh one, changing observable subscribe-reuse behavior, not just metadata shape.Found by automated SDK cross-language drift audit