Verified Production Fix
[nodejs/node] Errors thrown in `Readable.map` can get swallowed by `AbortError`
GH-nodejs/node#62089 • Mar 08, 2026
### ROOT CAUSE
The issue arises because the `Readable.map` method does not propagate errors properly when they occur. Instead, it can swallow errors and throw an `AbortError` if the stream is aborted. This behavior is not consistent with the expected behavior, where errors should be propagated up the pipeline.
### CODE FIX
To address this issue, you can create a custom transform stream that properly propagates errors. Here's a fix that demonstrates how to implement this:
js
import fs from 'node:fs';
import { Readable, Transform } from 'node:stream';
import { pipeline } from 'node:stream/promises';
class ErrorPropagatingTransform extends Transform {
constructor(options) {
super({ ...options, readableObjectMode: true });
}
_transform(chunk, encoding, callback) {
try {
for (let i = 0; i < this.transformPushCount; i++) {
this.push({});
}
callback();
} catch (error) {
callback(error);
}
}
}
export async function main({ sourceStream, transformPushCount }) {
await pipeline(
sourceStream,
new ErrorPropagatingTransform({ transformPushCount }),
(readable) =>
readable.map(async (obj) => {
throw Error('Boom!');
})
);
}
if (import.meta.main) {
try {
await main({
sourceStream: fs.createReadStream('/dev/urandom'),
transformPushCount: 1,
});
} catch (e) {
console.log(`sourceStream: file, transformPushCount: once, error: ${e}`);
}
try {
await main({
sourceStream: fs.createReadStream('/dev/urandom'),
transformPushCount: 2,
});
} catch (e) {
console.log(`sourceStream: file, transformPushCount: twice, error: ${e}`);
}
try {
await main({
sourceStream: Readable.from(['foo']),
transformPushCount: 1,
});
} catch (e) {
console.log(
`sourceStream: readable from iterable, transformPushCount: once, error: ${e}`
);
}
try {
await main({
sourceStream: Readable.from(['foo']),
transformPushCount: 2,
});
} catch (e) {
console.log(
`sourceStream: readable from iterable, transformPushCount: twice, error: ${e}`
);
}
try {
await main({
sourceStream: new Readable({
read(size) {
this.push('foo');
},
}),
transformPushCount: 1,
});
} catch (e) {
console.log(
`sourceStream: readable implementation, transformPushCount: once, error: ${e}`
);
}
try {
await main({
sourceStream: new Readable({
read(size) {
this.push('foo');
},
}),
transformPushCount: 2,
});
} catch (e) {
console.log(
`sourceStream: readable implementation, transformPushCount: twice, error: ${e}`
);
}
}
In this fix, a custom `ErrorPropagatingTransform` class is created, which extends `Transform`. The `_transform` method is overridden to handle errors properly by calling `callback(error)` when an error occurs. This ensures that errors are propagated up the pipeline, allowing them to be caught and handled appropriately.
Deploy with Vultr
Use this fix in production instantly. Claim your high-performance developer credit.
Get Started with Vultr →
digital