`fetch` may try to use a closed connection
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 7.7k
- Forks
- 880
- Avg merge
- 2d 16h
- Merged PRs (30d)
- 68
Description
Version
v22.6.0
Platform
Darwin MacBook-Pro-6.local 23.5.0 Darwin Kernel Version 23.5.0: Wed May 1 20:12:58 PDT 2024; root:xnu-10063.121.3~5/RELEASE_ARM64_T6000 arm64
Subsystem
http
What steps will reproduce the bug?
// fetch-test.js
const {createServer} = require('node:http');
const port = 8080;
const url = 'http://localhost:' + port;
const server = createServer((req, res) => res.end()).listen(port, async () => {
await fetch(url);
server.closeIdleConnections();
setImmediate(async () => {
await fetch(url); // Throws TypeError with cause UND_ERR_SOCKET or ECONNRESET
server.close();
});
});
How often does it reproduce? Is there a required condition?
Reproduces consistently for me but the error cause varies roughly evenly between ECONNRESET and UND_ERR_SOCKET (details below)
What is the expected behavior? Why is that the expected behavior?
fetch creates a new connection if there are none open, and the request succeeds.
What do you see instead?
node fetch-test.js
node:internal/deps/undici/undici:13178
Error.captureStackTrace(err);
^
TypeError: fetch failed
at node:internal/deps/undici/undici:13178:13
at async Immediate.<anonymous> (/Users/robhogan/workspace/fetch-test.js:11:5) {
[cause]: SocketError: other side closed
at Socket.<anonymous> (node:internal/deps/undici/undici:6020:28)
at Socket.emit (node:events:532:35)
at endReadableNT (node:internal/streams/readable:1696:12)
at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
code: 'UND_ERR_SOCKET',
socket: {
localAddress: '::1',
localPort: 57996,
remoteAddress: undefined,
remotePort: undefined,
remoteFamily: undefined,
timeout: undefined,
bytesWritten: 338,
bytesRead: 122
}
}
}
Node.js v22.6.0
OR
node fetch-test.js
node:internal/deps/undici/undici:13178
Error.captureStackTrace(err);
^
TypeError: fetch failed
at node:internal/deps/undici/undici:13178:13
at async Immediate.<anonymous> (/Users/robhogan/workspace/fetch-test.js:11:5) {
[cause]: Error: read ECONNRESET
at TCP.onStreamRead (node:internal/stream_base_commons:218:20) {
errno: -54,
code: 'ECONNRESET',
syscall: 'read'
}
}
Node.js v22.6.0
Additional information
This seems to be quite sensitive to timing/the event loop in a way I haven't pinned down.
- The
setImmediate(orsetTimeout(cb, 0)) is required to reproduce the issue. - Adding another
setImmediatebefore the secondfetchmakes it succeed. - Adding
{headers:{'Connection': 'close'}}to the first request succeeds.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the reproduction in fetch-test.js on the reported Node.js version, then inspect the HTTP and internal Undici paths shown in the stack trace. Compare connection handling around server.closeIdleConnections(), setImmediate(), and the second fetch; done means the second request succeeds without ECONNRESET or UND_ERR_SOCKET.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100