diff --git a/common/changes/@rushstack/node-core-library/fix-node-errno_2025-04-30-17-39.json b/common/changes/@rushstack/node-core-library/fix-node-errno_2025-04-30-17-39.json new file mode 100644 index 00000000000..bbeb22b0650 --- /dev/null +++ b/common/changes/@rushstack/node-core-library/fix-node-errno_2025-04-30-17-39.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/node-core-library", + "comment": "Fix a bug in `FileSystem.isErrnoException` that failed to identify errors if the underlying method was invoked using only a file descriptor, e.g. for `fs.readSync`.", + "type": "patch" + } + ], + "packageName": "@rushstack/node-core-library" +} \ No newline at end of file diff --git a/libraries/node-core-library/src/FileSystem.ts b/libraries/node-core-library/src/FileSystem.ts index 9f22dacef11..8572d8696c4 100644 --- a/libraries/node-core-library/src/FileSystem.ts +++ b/libraries/node-core-library/src/FileSystem.ts @@ -1508,10 +1508,11 @@ export class FileSystem { */ public static isErrnoException(error: Error): error is NodeJS.ErrnoException { const typedError: NodeJS.ErrnoException = error; + // Don't check for `path` because the syscall may not have a path. + // For example, when invoked with a file descriptor. return ( typeof typedError.code === 'string' && typeof typedError.errno === 'number' && - typeof typedError.path === 'string' && typeof typedError.syscall === 'string' ); } diff --git a/libraries/node-core-library/src/test/FileSystem.test.ts b/libraries/node-core-library/src/test/FileSystem.test.ts index 9789d3d3a6d..0712f630c38 100644 --- a/libraries/node-core-library/src/test/FileSystem.test.ts +++ b/libraries/node-core-library/src/test/FileSystem.test.ts @@ -1,25 +1,55 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import fs from 'node:fs'; + import { FileSystem } from '../FileSystem'; import { PosixModeBits } from '../PosixModeBits'; -// The PosixModeBits are intended to be used with bitwise operations. -/* eslint-disable no-bitwise */ +describe(FileSystem.name, () => { + test(FileSystem.formatPosixModeBits.name, () => { + // The PosixModeBits are intended to be used with bitwise operations. + /* eslint-disable no-bitwise */ + let modeBits: number = PosixModeBits.AllRead | PosixModeBits.AllWrite; + + expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rw-rw-'); + + modeBits |= PosixModeBits.GroupExecute; + expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rwxrw-'); -test('PosixModeBits tests', () => { - let modeBits: number = PosixModeBits.AllRead | PosixModeBits.AllWrite; + // Add the group execute bit + modeBits |= PosixModeBits.OthersExecute; + expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rwxrwx'); - expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rw-rw-'); + // Add the group execute bit + modeBits &= ~PosixModeBits.AllWrite; + expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-r--r-xr-x'); + /* eslint-enable no-bitwise */ + }); - modeBits |= PosixModeBits.GroupExecute; - expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rwxrw-'); + describe(FileSystem.isErrnoException.name, () => { + test('Should return false for a non-ErrnoException', () => { + const error: Error = new Error('Test error'); + expect(FileSystem.isErrnoException(error)).toBe(false); + }); - // Add the group execute bit - modeBits |= PosixModeBits.OthersExecute; - expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rwxrwx'); + test('Should return true for an error on a path call', () => { + expect.assertions(1); + try { + fs.openSync(`${__dirname}/nonexistent.txt`, 'r'); + } catch (error) { + expect(FileSystem.isErrnoException(error)).toBe(true); + } + }); - // Add the group execute bit - modeBits &= ~PosixModeBits.AllWrite; - expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-r--r-xr-x'); + test('Should return true for an error on a file descriptor call', () => { + const buffer: Buffer = Buffer.allocUnsafeSlow(1024); + expect.assertions(1); + try { + fs.readSync(11, buffer, 0, buffer.length, -1); + } catch (error) { + expect(FileSystem.isErrnoException(error)).toBe(true); + } + }); + }); });