diff --git a/lib/internal/fs/dir.js b/lib/internal/fs/dir.js index 32050f31ae6d..2aea78dad63c 100644 --- a/lib/internal/fs/dir.js +++ b/lib/internal/fs/dir.js @@ -130,21 +130,22 @@ class Dir { } if (this.#processHandlerQueue()) { + let dirent; try { - const dirent = ArrayPrototypeShift(this.#bufferedEntries); + dirent = ArrayPrototypeShift(this.#bufferedEntries); if (this.#options.recursive && dirent.isDirectory()) { this.#readSyncRecursive(dirent); } - - if (maybeSync) - process.nextTick(callback, null, dirent); - else - callback(null, dirent); - return; } catch (error) { return callback(error); } + + if (maybeSync) + process.nextTick(callback, null, dirent); + else + callback(null, dirent); + return; } const req = new FSReqCallback(); @@ -159,16 +160,17 @@ class Dir { return callback(err, result); } + let dirent; try { this.#processReadResult(this.#path, result); - const dirent = ArrayPrototypeShift(this.#bufferedEntries); + dirent = ArrayPrototypeShift(this.#bufferedEntries); if (this.#options.recursive && dirent.isDirectory()) { this.#readSyncRecursive(dirent); } - callback(null, dirent); } catch (error) { - callback(error); + return callback(error); } + callback(null, dirent); }; this.#operationQueue = []; diff --git a/test/parallel/test-fs-opendir-read-callback-throw.js b/test/parallel/test-fs-opendir-read-callback-throw.js new file mode 100644 index 000000000000..a591f51b9132 --- /dev/null +++ b/test/parallel/test-fs-opendir-read-callback-throw.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +const dir = tmpdir.resolve('opendir-cb-throw'); +fs.mkdirSync(dir); +fs.writeFileSync(`${dir}/a.txt`, ''); +fs.writeFileSync(`${dir}/b.txt`, ''); + +// `dir.read(callback)` used to invoke `callback` from inside a `try` block, so +// an exception thrown by user code inside the callback was caught and fed back +// into the same callback as if it were a directory read failure. The callback +// must be invoked exactly once, and only with the result of the read. + +process.on('uncaughtException', (err) => { + assert.strictEqual(err.message, 'thrown from the read callback'); +}); + +fs.opendir(dir, common.mustSucceed((d) => { + d.read(common.mustCall((err, dirent) => { + assert.strictEqual(err, null); + assert.notStrictEqual(dirent, null); + assert.match(dirent.name, /^[ab]\.txt$/); + throw new Error('thrown from the read callback'); + }, 1)); +}));