Uh oh!
There was an error while loading. Please reload this page.
fix(perps): clear clients when reconnect readiness fails - #9868
Conversation
#handleConnectionDrop builds all four SDK clients before the new transport reports ready, and the catch path left them in place. A reconnect whose ready() rejected therefore kept isInitialized() true, so callers gated on it issued WebSocket-backed reads over a socket that never opened instead of taking the uninitialized path. Clear the clients and transports on failure, mirroring the cleanup initialize() already performs, before the retry is scheduled. Before this, a failed reconnect reported isInitialized() true both after a plain drop (pre-existing) and after a disconnect (new, since all four clients are now recreated on the reconnect path).
6658fa4
into
fix/perps-client-not-initialized-reconnectionUh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Reviewed by Cursor Bugbot for commit 4df3413. Configure here.
| } | ||
| } | ||
| this.#wsTransport = undefined; | ||
| this.#httpTransport = undefined; |
There was a problem hiding this comment.
Reconnect cleanup races subscription heal
Medium Severity
Clearing #subscriptionClient on reconnect readiness failure makes ensureSubscriptionClient treat the service as cold and call initialize during the retry backoff. That can mark the session Connected without running #onReconnectCallback, so the scheduled #handleConnectionDrop retry sees Connected and skips — leaving previously tracked subscriptions unrestored.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 4df3413. Configure here.


Explanation
Addresses the reconnect-readiness finding raised in review on #9032; stacked on that branch.
#handleConnectionDropconstructs all four SDK clients beforeawait newWsTransport.ready(). The catch path left them in place, so a reconnect whose readiness rejected keptisInitialized()returningtrue. Callers gated onisInitialized()/ensureInitialized()then issued WebSocket-backed reads (getInfoClient,getSubscriptionClient) over a socket that never opened, instead of taking the uninitialized path. Writes were unaffected —ExchangeClientruns on the HTTP transport.This clears the clients and both transports on failure, mirroring the cleanup
initialize()already performs, before the retry is scheduled. The retry then rebuilds them from scratch as it already did.Scope of the regression
Measured with a rejected
ready()on the reconnect path:0837703(before #9032)322eda6initialize()isInitialized()truedisconnect()isInitialized()falseSo the post-disconnect row is the behaviour change introduced by recreating all four clients on the reconnect path; the plain-drop row was already wrong before and is fixed here too.
Test
HyperLiquidClientService.test.tsgains two cases under the reconnection describe: readiness rejects after a plaininitialize(), and after adisconnect(). Both assertisInitialized() === false(the first also assertsgetSubscriptionClient()isundefined). Both fail on the branch without this change (Received: true) and pass with it.Validation
jest tests/src/services tests/src/providers— 34 suites, 1484 passed, 0 failedeslinton both changed files — cleanReferences
Checklist
Note
Medium Risk
Touches WebSocket reconnection lifecycle in a critical trading client path; behavior change is narrow (fail-closed on failed ready) but affects when reads proceed after a bad reconnect.
Overview
Fixes a reconnect edge case where HyperLiquid SDK clients are built before
WebSocketTransport.ready()completes. Ifready()rejects, the catch path used to leave those clients in place, soisInitialized()stayed true even though the socket never opened—callers gated on initialization could issue WebSocket-backed reads on a dead connection instead of failing closed or retrying.On reconnect failure,
#handleConnectionDropnow clears all four SDK clients and both transports (with safeclose()on the WS transport), matching the cleanupinitialize()already does on failure, before scheduling the existing retry.Tests cover readiness rejection after a normal
initialize()and afterdisconnect(); both assertisInitialized() === false. Changelog updated under Fixed.Reviewed by Cursor Bugbot for commit 4df3413. Bugbot is set up for automated code reviews on this repo. Configure here.