Unhandled 'error' event when using AbortSignal to cancel requests
Reproduction
Steps to reproduce the behavior:
const controller = new AbortController();
const fetchTimeout = setTimeout(() => {
controller.abort();
}, 500); // Try different values from 0 - 3000
const requestOptions = {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({test: 'test'}),
signal: controller.signal
};
try {
const response = await fetch(url, requestOptions)
} catch (err) {
// handle error
}
The problem is somehow intermittent. Try different timeout values until you can reproduce it. I get the following error:
node:events:368
throw er; // Unhandled 'error' event
^
AbortError: The operation was aborted.
at abort (file:///D:/xampp/htdocs/fetch_retry_test/node_modules/node-fetch/src/index.js:70:18)
at file:///D:/xampp/htdocs/fetch_retry_test/node_modules/node-fetch/src/index.js:87:4
at new Promise (<anonymous>)
at fetch (file:///D:/xampp/htdocs/fetch_retry_test/node_modules/node-fetch/src/index.js:49:9)
at wrappedFetch (D:\xampp\htdocs\fetch_retry_test\node_modules\fetch-retry\index.js:71:9)
at Timeout._onTimeout (D:\xampp\htdocs\fetch_retry_test\node_modules\fetch-retry\index.js:127:11)
at listOnTimeout (node:internal/timers:557:17)
at processTimers (node:internal/timers:500:7)
Emitted 'error' event on Readable instance at:
at emitErrorNT (node:internal/streams/destroy:157:8)
at emitErrorCloseNT (node:internal/streams/destroy:122:3)
at processTicksAndRejections (node:internal/process/task_queues:83:21) {
type: 'aborted'
}
Expected behavior The error happens in index.js in the following code block:
const abort = () => {
const error = new AbortError('The operation was aborted.');
reject(error);
if (request.body && request.body instanceof Stream.Readable) {
request.body.destroy(error); // <==== HERE IS WHERE THE UNHANDLED ERROR OCCURS!!!!
}
if (!response || !response.body) {
return;
}
response.body.emit('error', error);
};
To fix it, you need a listener for the "error" event of the Readable stream, like this below:
const abort = () => {
const error = new AbortError('The operation was aborted.');
reject(error);
if (request.body && request.body instanceof Stream.Readable) {
request.body.on('error', ()=>{}); // You must define a listener so that potential errors when destroying the stream ar catched
request.body.destroy(error);
}
if (!response || !response.body) {
return;
}
response.body.emit('error', error);
};
Screenshots
Your Environment
| software | version |
|---|---|
| node-fetch | 3.3.2 |
| node | 16.13.0 |
| npm | 8.1.0 |
| Operating System | Windows 10 |
Additional context I believe this issue was reported in the past, but it was closed: https://github.com/node-fetch/node-fetch/issues/1420
I had the same issue. In the end, I had to stop using node-fetch and instead used the native fetch API. The drawback is that it returns a (web) ReadableStream and not a (Node) stream.Readable.
You'll have to use it like this
const reader = stream.getReader();
while (true) {
try {
const { value, done } = await reader.read();
if (done) {
break;
}
// Do something
} catch(err) {
console.log('Error reading stream', err);
break;
}
}
Happened to me as well, any resolution for this?