Summary
Since RN 0.79, HEARTBEAT_TIMEOUT_MS = 60_000 in @react-native/dev-middleware's InspectorProxy.js causes Metro to terminate the device WebSocket when the iOS app is backgrounded for ~60 seconds, killing the active debug session. This is a regression introduced by #49618.
Environment
- React Native: 0.81.5
@react-native/dev-middleware: 0.81.5
- iOS device (real, not simulator)
- Hermes engine
Reproduction
- Run a debug session against an iOS device build (e.g. via
vscode-react-native extension's reactnativedirect attach)
- Confirm Hermes is connected and breakpoints work
- Switch from the app to another app on the device (or send to background)
- Wait ~60 seconds
- Debug session terminates
Root cause
- iOS suspends the app's JS thread shortly after backgrounding (standard iOS behavior)
- Hermes can no longer respond to WebSocket Ping frames sent by Metro
- Metro's
InspectorProxyHeartbeat fires socket.terminate() after 60s timeout
Device.js close handler emits [CONNECTION_LOST] Connection lost to corresponding device to debugger
- VSCode debug session terminates
Evidence
TCP-level packet capture shows the iPhone's TCP stack remains responsive throughout (sending ACKs to all data Metro pushes), proving the network connection itself is not lost — only the application-level Pong is missing because Hermes is suspended:
| Time |
Event |
| t=0s |
iOS app sent to background |
| t=0–60s |
Mac sends Text + Ping frames; iPhone replies with TCP ACKs but no WS Pong |
| t=~60s |
Metro fires socket.terminate(), sends FIN |
| t=~60s |
Inspector proxy emits CONNECTION_LOST to debugger |
Why this is a regression
The PR #49618 description explicitly states:
"It is a no-op because the WebSocket on the Device is implemented by us and is not supposed to drop connections like the browser does."
This assumption doesn't hold for iOS apps where the OS pauses the JS thread on backgrounding. Pre-#49618 setups did not terminate the device socket, so debugging survived backgrounding/foregrounding cycles.
Proposal
Options (any/all):
- Make
HEARTBEAT_TIMEOUT_MS configurable via Metro config or env var
- Default to a much higher timeout in dev mode (e.g. 24h) — the original goal of cleaning up "abandoned" remote devices is still met if the dev never returns
- Treat ping timeout as a "stale" marker without actively terminating, allowing reconnect when the device wakes
- Detect iOS app lifecycle hints (if available) and pause the heartbeat while the device is suspended
The current behavior breaks common dev workflows (switching to other apps to test deep links, payments, OAuth, etc.).
Workaround
Patch HEARTBEAT_TIMEOUT_MS via patch-package / bun patch to a high value (e.g. 24h).
Summary
Since RN 0.79,
HEARTBEAT_TIMEOUT_MS = 60_000in@react-native/dev-middleware'sInspectorProxy.jscauses Metro to terminate the device WebSocket when the iOS app is backgrounded for ~60 seconds, killing the active debug session. This is a regression introduced by #49618.Environment
@react-native/dev-middleware: 0.81.5Reproduction
vscode-react-nativeextension'sreactnativedirectattach)Root cause
InspectorProxyHeartbeatfiressocket.terminate()after 60s timeoutDevice.jsclose handler emits[CONNECTION_LOST] Connection lost to corresponding deviceto debuggerEvidence
TCP-level packet capture shows the iPhone's TCP stack remains responsive throughout (sending ACKs to all data Metro pushes), proving the network connection itself is not lost — only the application-level Pong is missing because Hermes is suspended:
socket.terminate(), sends FINWhy this is a regression
The PR #49618 description explicitly states:
This assumption doesn't hold for iOS apps where the OS pauses the JS thread on backgrounding. Pre-#49618 setups did not terminate the device socket, so debugging survived backgrounding/foregrounding cycles.
Proposal
Options (any/all):
HEARTBEAT_TIMEOUT_MSconfigurable via Metro config or env varThe current behavior breaks common dev workflows (switching to other apps to test deep links, payments, OAuth, etc.).
Workaround
Patch
HEARTBEAT_TIMEOUT_MSviapatch-package/bun patchto a high value (e.g. 24h).