Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion lib/internal/fs/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,11 @@ class FileHandle extends EventEmitter {
} = require('internal/webstreams/readablestream');
this[kRef]();
this.once('close', () => {
readableStreamCancel(readable);
PromisePrototypeThen(
readableStreamCancel(readable),
undefined,
() => undefined,
);
});

return readable;
Expand Down
82 changes: 82 additions & 0 deletions test/parallel/test-filehandle-readablestream-error-close.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
'use strict';

const common = require('../common');
const assert = require('assert');

async function runScript(source) {
const child = await common.spawnPromisified(process.execPath, [
'--unhandled-rejections=strict',
'-e',
source,
]);

assert.strictEqual(child.code, 0, child.stderr);
assert.strictEqual(child.signal, null);
assert.strictEqual(child.stderr, '');
}

// Regression: once a readableWebStream read fails, explicitly closing the
// FileHandle must not trigger an unhandled rejection.
(async () => {
await runScript(`
const { closeSync } = require('node:fs');
const { open } = require('node:fs/promises');
const assert = require('node:assert');

async function consume(readable) {
for await (const _ of readable);
}

(async () => {
const file = await open(process.execPath);
const readable = file.readableWebStream();
closeSync(file.fd);

await assert.rejects(consume(readable), { code: 'EBADF' });
await assert.rejects(file.close(), { code: 'EBADF' });
})().catch((err) => {
console.error(err);
process.exitCode = 1;
});
`);
})().then(common.mustCall());

// Edge: BYOB readers should not leak unhandled rejections on the same path.
(async () => {
await runScript(`
const { closeSync } = require('node:fs');
const { open } = require('node:fs/promises');
const assert = require('node:assert');

(async () => {
const file = await open(process.execPath);
const readable = file.readableWebStream();
const reader = readable.getReader({ mode: 'byob' });
closeSync(file.fd);

await assert.rejects(reader.read(new DataView(new ArrayBuffer(1024))), {
code: 'EBADF',
});
await assert.rejects(file.close(), { code: 'EBADF' });
})().catch((err) => {
console.error(err);
process.exitCode = 1;
});
`);
})().then(common.mustCall());

// Safety: successful reads must remain unaffected.
(async () => {
await runScript(`
const { open } = require('node:fs/promises');

(async () => {
const file = await open(process.execPath);
for await (const _ of file.readableWebStream());
await file.close();
})().catch((err) => {
console.error(err);
process.exitCode = 1;
});
`);
})().then(common.mustCall());