Skip to content

New ECONNRESET error on http.request for HTTP 413 (Node v24.16.0 regression) #64272

Description

@Krinkle

Version

24.18.0

Platform

Linux

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions