Version
24.18.0
Platform
Subsystem
http
What steps will reproduce the bug?
Reproducible with the following standalone snippet:
/* eslint-env qunit */
'use script';
const http = require('http');
async function request (url, options) {
return new Promise((resolve, reject) => {
const req = http.request(url, {
method: options.method,
headers: options.headers
}, (response) => {
resolve(response);
});
// req.on('error', reject); // Adding this makes it work
req.write(options.body);
req.end();
});
}
(async function() {
const server = http.createServer();
server.on('request', (request, response) => {
console.log('server-request', request.url, request.headers);
const MAX_BODY_LENGTH = 1024 * 1024;
let body = '';
request.setEncoding('utf8');
request.on('data', function onData (chunk) {
body += chunk;
if (body.length > MAX_BODY_LENGTH) {
// HTTP 413 Payload Too Large
response.writeHead(413);
response.end();
request.off('data', onData);
request.destroy();
}
});
request.on('end', function onEnd () {
response.writeHead(202);
response.end();
});
});
server.on('error', (err) => console.error('server-error', err));
server.listen(0);
await new Promise((resolve) => server.on('listening', resolve()));
const address = `http://localhost:${server.address().port}`;
const options = {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
a: 'x'.repeat(512 * 1024),
b: 'x'.repeat(512 * 1024),
c: 'x'.repeat(512 * 1024),
d: 'x'.repeat(512 * 1024)
}, null, '\t')
};
try {
// Works fine on Node v24.18.0
// const resp = await fetch(address, options);
// const data = await resp.text();
// console.log(resp, data);
// Works from Node v10.x to v24.15.x and fails in v24.16.x, v24.17.x, v24.18.x
const resp = await request(address, options);
console.log(resp.statusCode);
} catch (e) {
console.error(e);
} finally {
server.close();
}
}());
How often does it reproduce? Is there a required condition?
Every time. No race condition.
What is the expected behavior? Why is that the expected behavior?
From Node v10 to v24.15.x this worked without any error.
What do you see instead?
Starting in v24.16.x (including v24.17.x and latest v24.18.x) the client consistently experiences ECONNRESET.
node:events:487
throw er; // Unhandled 'error' event
^
Error: read ECONNRESET
at TCP.onStreamRead (node:internal/stream_base_commons:216:20)
Emitted 'error' event on ClientRequest instance at:
at emitErrorEvent (node:_http_client:109:11)
at Socket.socketErrorListener (node:_http_client:593:5)
at Socket.emit (node:events:509:28)
at emitErrorNT (node:internal/streams/destroy:170:8)
at emitErrorCloseNT (node:internal/streams/destroy:129:3)
at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {
errno: -104,
code: 'ECONNRESET',
syscall: 'read'
}
I can and should handle this, although it is a no-op in practice because the Promise is already settled by resolve() with an HTTP 413 response.
Unclear if a regression or intentional. I imagine this kind of naive request() implementation is a fairly common idiom for simple cases where no error is expected. Either in code that still needs to support Node.js 10 or where one simply hasn't yet had a reason to revisit it and switch to fetch().
Additional information
I found this when attempting to add Node.js 24 support to https://github.com/jquery/node-notifier-server/ which is a server for processing GitHub Webhooks.
https://github.com/jquery/node-notifier-server/actions/runs/28656799866
https://github.com/jquery/node-notifier-server/actions/runs/28657579964
QUnit result:
TAP version 13
# [notifier-server:debug] The notifier-server is listening on port 33765
ok 1 notifier-server > start server
…
# [notifier-server:debug] Including /tmp/notifiergY1g53/example.js
# [notifier-server:debug] The notifier-server is listening on port 35017
not ok 15 notifier-server > notifier ignores too large json
---
message: "global failure: Error: read ECONNRESET"
severity: failed
stack: |
Error: read ECONNRESET
at TCP.onStreamRead (node:internal/stream_base_commons:216:20)
...
1..15
# pass 14
# skip 0
# todo 0
# fail 1
Error: Process completed with exit code 1.
Version
24.18.0
Platform
Subsystem
http
What steps will reproduce the bug?
Reproducible with the following standalone snippet:
How often does it reproduce? Is there a required condition?
Every time. No race condition.
What is the expected behavior? Why is that the expected behavior?
From Node v10 to v24.15.x this worked without any error.
What do you see instead?
Starting in v24.16.x (including v24.17.x and latest v24.18.x) the client consistently experiences ECONNRESET.
I can and should handle this, although it is a no-op in practice because the Promise is already settled by
resolve()with an HTTP 413 response.Unclear if a regression or intentional. I imagine this kind of naive
request()implementation is a fairly common idiom for simple cases where no error is expected. Either in code that still needs to support Node.js 10 or where one simply hasn't yet had a reason to revisit it and switch tofetch().Additional information
I found this when attempting to add Node.js 24 support to https://github.com/jquery/node-notifier-server/ which is a server for processing GitHub Webhooks.
https://github.com/jquery/node-notifier-server/actions/runs/28656799866
https://github.com/jquery/node-notifier-server/actions/runs/28657579964
QUnit result: