Description
The following issue was found using the Claude Opus 4.6 LLM and verified by me manually. I've used Claude to come up with a fix in the React Native Java code, then monkey patched the RN source files and built an app with that fix.
Before the fix whenever I tried to get the snapshot through the Debugger, Chrome Dev Tools or even a custom Node script it would fail. When using the node script I would get around 70MB of snapshot and then the WS connection would close leaving me with an incomplete snapshot file. By inspecting the internal metro code during that I found that the socket connection to the device was closing with the 1001 code.
After applying the fix Claude came up with both the Debugger and the Node script were able to get the snapshot without any issues.
The final snapshot I extracted from the app was around 120 MB.
I've confirmed this issue to be present in the 0.83. which is the latest version I am able to upgrade the project in which I ran into this issue. The fix that ended up helping I only validated on 0.79.6
Summary generated by Claude
React Native version: 0.79.6 (unpatched as of main branch)
Platform: Android only
Description
Taking a JS heap snapshot from a React Native Android app via Hermes CDP always fails for large heaps (>100MB snapshot size). The WebSocket connection between the app and Metro closes with code 1001 partway through the snapshot — typically after a portion of chunks are delivered — causing the snapshot to be incomplete or the tooling to throw WS closed before snapshot read was completed.
Root cause
CxxInspectorPackagerConnection.java uses OkHttp to maintain the WebSocket from the app to Metro. OkHttp's RealWebSocket has a hardcoded MAX_QUEUE_SIZE = 16 * 1024 * 1024 (16 MiB). When HeapProfiler.takeHeapSnapshot is triggered, Hermes streams chunks to the inspector faster than TCP delivers them to Metro. The outgoing OkHttp queue fills past 16 MiB, and OkHttp closes the connection with code 1001.
A second issue compounds it: the OkHttpClient is configured with writeTimeout(10, TimeUnit.SECONDS). Since heap snapshots can take more than 10 seconds to fully flush, the write timeout can also trigger disconnection independently.
Relevant file: ReactAndroid/src/main/java/com/facebook/react/devsupport/CxxInspectorPackagerConnection.java
// Current code (unfixed):
private final OkHttpClient mHttpClient =
new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS) // ← kills long snapshots
.readTimeout(0, TimeUnit.MINUTES)
.build();
// ...in connectWebSocket():
return new IWebSocket() {
@Override
public void send(String message) {
webSocket.send(message); // ← no backpressure, overflows MAX_QUEUE_SIZE
}
// ...
};
Fix
Two changes in connectWebSocket():
- Set writeTimeout(0, TimeUnit.SECONDS) to disable the write timeout (matching how readTimeout is already disabled).
- Introduce an unbounded local queue with a sender thread that throttles sends to keep OkHttp's internal queue below its 16 MiB hard limit:
.writeTimeout(0, TimeUnit.SECONDS) // was 10s — disabled to match readTimeout
// In connectWebSocket(), replace direct webSocket.send() with:
final BlockingQueue<String> localQueue = new LinkedBlockingQueue<>();
final AtomicBoolean closing = new AtomicBoolean(false);
final long OKHTTP_QUEUE_HIGH_WATERMARK = 8 * 1024 * 1024; // stay under 16 MiB hard limit
Thread senderThread = new Thread(() -> {
while (!closing.get()) {
try {
String message = localQueue.poll(1, TimeUnit.SECONDS);
if (message == null) continue;
while (webSocket.queueSize() > OKHTTP_QUEUE_HIGH_WATERMARK) {
Thread.sleep(5);
}
webSocket.send(message);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}, "InspectorWebSocketSender");
senderThread.setDaemon(true);
senderThread.start();
return new IWebSocket() {
@Override public void send(String message) { localQueue.offer(message); }
@Override public void close() {
closing.set(true);
webSocket.close(1000, "End of session");
}
};
Steps to reproduce
- Run a React Native Android app in debug mode with a large heap
- Connect to Metro via CDP and call HeapProfiler.takeHeapSnapshot
- Observe connection closes before the whole snapshot is delivered. code 1001 is given by the socket (node_modules/@react-native/dev-middleware/dist/inspector-proxy/InspectorProxy.js:232)
React Native Version
0.83.0
Output of npx @react-native-community/cli info
System:
OS: Linux 6.18 Manjaro Linux
CPU: (8) x64 Intel(R) Core(TM) i7-10610U CPU @ 1.80GHz
Memory: 2.76 GB / 30.97 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 22.22.0
path: /home/owner/.nvm/versions/node/v22.22.0/bin/node
Yarn:
version: 1.22.22
path: /usr/local/bin/yarn
npm:
version: 10.9.4
path: /home/owner/.nvm/versions/node/v22.22.0/bin/npm
Watchman: Not Found
SDKs:
Android SDK:
Android NDK: 29.0.14206865
IDEs:
Android Studio: AI-253.30387.90.2532.14935130
Languages:
Java:
version: 25.0.2
path: /usr/bin/javac
Ruby:
version: 3.4.8
path: /usr/bin/ruby
npmPackages:
"@react-native-community/cli": Not Found
react:
installed: 19.2.0
wanted: 19.2.0
react-native:
installed: 0.83.0
wanted: 0.83.0
npmGlobalPackages:
"*react-native*": Not Found
Android:
hermesEnabled: Not found
newArchEnabled: Not found
iOS:
hermesEnabled: Not found
newArchEnabled: Not found
Screenshots and Videos
No response
Maybe related to
#39651
#49158
Repro
This would likely be possible to be reproduced with this: https://github.com/abbasvlb/heapdumpissue/
although I would use a higher value than 20MB
Description
The following issue was found using the Claude Opus 4.6 LLM and verified by me manually. I've used Claude to come up with a fix in the React Native Java code, then monkey patched the RN source files and built an app with that fix.
Before the fix whenever I tried to get the snapshot through the Debugger, Chrome Dev Tools or even a custom Node script it would fail. When using the node script I would get around 70MB of snapshot and then the WS connection would close leaving me with an incomplete snapshot file. By inspecting the internal metro code during that I found that the socket connection to the device was closing with the 1001 code.
After applying the fix Claude came up with both the Debugger and the Node script were able to get the snapshot without any issues.
The final snapshot I extracted from the app was around 120 MB.
I've confirmed this issue to be present in the 0.83. which is the latest version I am able to upgrade the project in which I ran into this issue. The fix that ended up helping I only validated on 0.79.6
Summary generated by Claude
React Native version: 0.79.6 (unpatched as of main branch)
Platform: Android only
Description
Taking a JS heap snapshot from a React Native Android app via Hermes CDP always fails for large heaps (>100MB snapshot size). The WebSocket connection between the app and Metro closes with code 1001 partway through the snapshot — typically after a portion of chunks are delivered — causing the snapshot to be incomplete or the tooling to throw WS closed before snapshot read was completed.
Root cause
CxxInspectorPackagerConnection.java uses OkHttp to maintain the WebSocket from the app to Metro. OkHttp's RealWebSocket has a hardcoded MAX_QUEUE_SIZE = 16 * 1024 * 1024 (16 MiB). When HeapProfiler.takeHeapSnapshot is triggered, Hermes streams chunks to the inspector faster than TCP delivers them to Metro. The outgoing OkHttp queue fills past 16 MiB, and OkHttp closes the connection with code 1001.
A second issue compounds it: the OkHttpClient is configured with writeTimeout(10, TimeUnit.SECONDS). Since heap snapshots can take more than 10 seconds to fully flush, the write timeout can also trigger disconnection independently.
Relevant file: ReactAndroid/src/main/java/com/facebook/react/devsupport/CxxInspectorPackagerConnection.java
// Current code (unfixed):
// ...in connectWebSocket():
Fix
Two changes in connectWebSocket():
Steps to reproduce
React Native Version
0.83.0
Output of
npx @react-native-community/cli infoScreenshots and Videos
No response
Maybe related to
#39651
#49158
Repro
This would likely be possible to be reproduced with this: https://github.com/abbasvlb/heapdumpissue/
although I would use a higher value than 20MB