React Native: no way to recover a suspended (zombie) WebSocket faster than the hardcoded 60s inactivity timeout
Environment: convex 1.45.0 / 1.46.0, Expo SDK 54, React Native 0.81, iOS 17–26
Problem
When iOS suspends an app, the WebSocket is frozen without a close event. On resume the client still reports connectionState().isWebSocketConnected === true, but the socket is dead: queries hang and no Transition/Ping arrives. The only recovery path is serverInactivityThreshold (60s, hardcoded in WebSocketManager), after which closeAndReconnect("InactiveServer") runs with 1s→16s exponential backoff.
The client's only environment listener is window.addEventListener("online"), which React Native never fires (setupNetworkListener correctly returns early there). There is no AppState/NetInfo equivalent, and closeAndReconnect / tryReconnectImmediately are private.
For a field-service app where techs reopen the app every few minutes on cellular, this shows up as 60s+ of "loading" per reopen, and on marginal signal (backoff + full resubscribe restarting after a drop) it stretches to minutes. We measured one device doing ~160 full resubscribes in a workday, many of them 30–90s apart with no app-open event — i.e. the inactivity timer firing on a socket that had silently died.
Current workaround
Fragile, reaches into client.sync.webSocketManager: on AppState → active and on NetInfo reachability changes, run a one-shot query with a unique nonce (so it can't be served from the local cache); if it doesn't resolve within 5s, call the private closeAndReconnect. pause()/resume() don't help here because resume() on a ready socket resends the query set over the same dead socket.
Asks (any one would let us delete the hack)
- A public
client.reconnect()(or maketryReconnectImmediately/closeAndReconnectpublic), so app code that does know about lifecycle events can drive recovery. - A constructor option for
serverInactivityThreshold(e.g. 10s on mobile). - Built-in React Native lifecycle awareness: on
AppState→active, send a ping and reconnect if no server message arrives within a short window.
Happy to test a prerelease against real devices.
Source: get-convex/convex-backend