Dev server exits on any WebSocket error: InspectorProxy never listens for 'error'
Ninguém assumiu esta issue ainda.
Avaliação
- Dificuldade
- 2/5
- Tempo estimado
- 1-3 horas
- Facilidade para iniciantes
- 88/100
- Tipo de issue
- Bug
- Clareza
- Claramente especificada
- Status de atividade
- Ativa
- Stack de tecnologia
- javascript, react-native
Direção de pesquisa
Comece em packages/dev-middleware/src/inspector-proxy/InspectorProxy.js, nos handlers de conexão próximos às linhas 336 e 525, e compare como cada WebSocket trata os eventos de mensagem e fechamento. Execute o reproducer autocontido da issue; está concluído quando os dois caminhos de conexão tratarem os erros do socket sem encerrar o servidor de desenvolvimento, e o reproducer imprimir que o proxy sobreviveu.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
Description
InspectorProxy never attaches an "error" listener to the WebSocket connections it accepts. In Node, an 'error' event with no listener throws — so a socket-level failure on one connection takes down the entire dev server, not just that connection.
Both connection handlers listen for "message" and "close" only. On main today:
packages/dev-middleware/src/inspector-proxy/InspectorProxy.js#L336—#createDeviceConnectionWSServerpackages/dev-middleware/src/inspector-proxy/InspectorProxy.js#L525—#createDebuggerConnectionWSServer
I hit this twice in one working session, roughly 4.5 hours into each run, on an otherwise idle project with a simulator attached. Metro exits with:
RangeError: Too many message fragments
at Receiver.getData (.../@react-native/dev-middleware/node_modules/ws/lib/receiver.js:359:14)
at Receiver.startLoop (.../ws/lib/receiver.js:158:22)
at Receiver._write (.../ws/lib/receiver.js:94:10)
at Socket.socketOnData (.../ws/lib/websocket.js:882:35)
Emitted 'error' event on WebSocket instance at:
at Receiver.receiverOnError (.../ws/lib/websocket.js:787:13) {
[Symbol(status-code)]: 1008
}
Two things are tangled here, and I want to be clear that only one is a bug:
-
The
RangeErroritself comes from React Native's vendored, patchedws@6.2.5, which adds caps upstreamwsdoes not have —maxFragments: 16 * 1024andmaxBufferedChunks: 256 * 1024(ws/lib/websocket.js). That looks like deliberate hardening and I am not suggesting it be relaxed.InspectorProxysetsmaxPayload: 0on both servers but leavesmaxFragmentsat that default, which seems intentional. -
The bug is that tripping it is fatal to the process rather than to the connection. The fragment cap is just the error this project happened to hit; any socket-level error on either server has the same effect. A dev server should not be killable by one misbehaving WebSocket peer.
Steps to reproduce
Self-contained, no app and no Expo required:
mkdir repro && cd repro && npm init -y
npm i @react-native/dev-middleware@0.81.5 ws
node repro.js
// repro.js
const http = require('http');
const WS = require('ws');
const {createDevMiddleware} = require('@react-native/dev-middleware');
const server = http.createServer();
const {middleware, websocketEndpoints} = createDevMiddleware({
projectRoot: __dirname,
serverBaseUrl: 'http://localhost:8099',
});
server.on('request', middleware);
server.on('upgrade', (req, socket, head) => {
const {pathname} = new URL(req.url, 'http://localhost');
const wss = websocketEndpoints[pathname];
if (!wss) return socket.destroy();
wss.handleUpgrade(req, socket, head, ws => wss.emit('connection', ws, req));
});
server.listen(8099, () => {
const ws = new WS(
'ws://localhost:8099/inspector/device?device=1&name=repro&app=com.example',
);
ws.on('open', () => {
// One message split into more fragments than the vendored ws allows.
const s = ws._sender;
s.send(Buffer.from('x'), {fin: false, opcode: 1, mask: true}, () => {});
for (let i = 0; i < 17000; i++) {
s.send(Buffer.from('x'), {fin: false, opcode: 0, mask: true}, () => {});
}
s.send(Buffer.from('x'), {fin: true, opcode: 0, mask: true}, () => {});
});
ws.on('error', () => {});
setTimeout(() => {
console.log('PROXY SURVIVED — connection dropped, server still up.');
process.exit(0);
}, 6000);
});
Expected: the proxy drops that connection and keeps serving; PROXY SURVIVED prints.
Actual: the process dies with the unhandled 'error' event and the stack above. PROXY SURVIVED never prints.
Note the reproducer forces the error deterministically in seconds rather than waiting hours. In the wild it arrived on its own from a normally-connected iOS simulator.
The fix
Attaching an "error" listener at the top of both connection handlers is enough. It has to be attached synchronously, before the first await — these callbacks are async, so an error arriving mid-await would otherwise still be unhandled:
wss.on('connection', async (socket: WS, req) => {
socket.on('error', error => {
this.#logger?.error('Error on device connection, closing it: %s', error?.message ?? String(error));
// terminate() rather than close(): close() waits for a closing handshake,
// and a socket that failed mid-frame may never complete one.
try { socket.terminate(); } catch {}
});
// ...
Verified locally as a patch-package patch against 0.81.5: with the listener, the same reproducer prints PROXY SURVIVED, and a real project's Metro then starts, builds its iOS bundle, and serves a connected simulator normally.
Happy to open a PR if that would help.
React Native Version
0.81.5
Output of npx @react-native-community/cli info
System:
OS: macOS 27.0
CPU: (12) arm64 Apple M3 Pro
Memory: 58.52 MB / 18.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 22.12.0
path: /usr/local/bin/node
Yarn: Not Found
npm:
version: 11.0.0
path: /usr/local/bin/npm
Watchman: Not Found
Managers:
CocoaPods:
version: 1.17.0
path: /opt/homebrew/bin/pod
SDKs:
iOS SDK:
Platforms:
- DriverKit 25.5
- iOS 26.5
- macOS 26.5
- tvOS 26.5
- visionOS 26.5
- watchOS 26.5
Android SDK: Not Found
IDEs:
Android Studio: Not Found
Xcode:
version: 26.6/17F113
path: /usr/bin/xcodebuild
Languages:
Java: Not Found
Ruby:
version: 2.6.10
path: /usr/bin/ruby
npmPackages:
"@react-native-community/cli": Not Found
react:
installed: 19.1.0
wanted: 19.1.0
react-native:
installed: 0.81.5
wanted: 0.81.5
react-native-macos: Not Found
Notes on scope
The project I hit this on uses Expo, and the template asks Expo users to file with Expo first. I have filed here rather than there deliberately: the missing listener is in @react-native/dev-middleware, the reproducer above installs that package directly and involves no Expo code, and the two wss.on('connection', ...) handlers on main still have no "error" listener. Happy to move it if you disagree.
Screenshots and Videos
n/a — the reproducer output above is the whole symptom.
- Linguagem predominante
- C++
- Estrelas
- 127k
- Forks
- 25.3k
- Métricas de merge de PRs
- Nenhum PR com merge em 30d
Guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de react/react-native
-
Needs: Author Feedback Needs: Repro
Dificuldade 1/5 Menos de uma hora Facilidade para iniciantes 92/100
react/react-native#58621 · 1 comentário ·
-
Needs: Author Feedback Needs: Repro
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 85/100
react/react-native#58610 · 1 comentário ·
-
Needs: Triage :mag:
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 82/100
react/react-native#58565 · 1 comentário · 2 reações ·
-
Needs: Author Feedback Needs: Repro
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 88/100
react/react-native#58555 · 4 comentários · 1 reação ·
-
Needs: Attention Needs: Repro
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 85/100
react/react-native#58526 · 2 comentários ·
Todas as issues de react/react-native
Issues semelhantes
-
enhancement
Dificuldade 1/5 Menos de uma hora Facilidade para iniciantes 88/100
QuantStack/git2cpp#187 ·
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 86/100
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 84/100
mlcommons/mobile_app_open#1182 ·
-
Needs-Triage
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
microsoft/winget-cli#6547 ·
-
Dificuldade 1/5 Menos de uma hora Facilidade para iniciantes 90/100
AXERA-TECH/ax-llm#77 ·