From 489b0f974c9d1f6a0754ac646c59f7e420641c94 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 2 Sep 2025 14:45:56 +0200 Subject: [PATCH] url: add err.input to ERR_INVALID_FILE_URL_PATH Otherwise there's no information from the error about what exactly is the invalid URL. --- doc/api/errors.md | 3 +++ lib/internal/errors.js | 5 +++- lib/internal/url.js | 7 +++--- test/parallel/test-url-fileurltopath.js | 18 ------------- .../test-url-invalid-file-url-path-input.js | 25 +++++++++++++++++++ 5 files changed, 36 insertions(+), 22 deletions(-) create mode 100644 test/parallel/test-url-invalid-file-url-path-input.js diff --git a/doc/api/errors.md b/doc/api/errors.md index 99d5c76cc90a52..2346c1c53c6e6e 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1996,6 +1996,9 @@ A Node.js API that consumes `file:` URLs (such as certain functions in the [`fs`][] module) encountered a file URL with an incompatible path. The exact semantics for determining whether a path can be used is platform-dependent. +The thrown error object includes an `input` property that contains the URL object +of the invalid `file:` URL. + ### `ERR_INVALID_HANDLE_TYPE` diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 2453d8b0e9d1a0..23a54754e2a321 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1489,7 +1489,10 @@ E('ERR_INVALID_FD', E('ERR_INVALID_FD_TYPE', 'Unsupported fd type: %s', TypeError); E('ERR_INVALID_FILE_URL_HOST', 'File URL host must be "localhost" or empty on %s', TypeError); -E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s', TypeError); +E('ERR_INVALID_FILE_URL_PATH', function(reason, input) { + this.input = input; + return `File URL path ${reason}`; +}, TypeError); E('ERR_INVALID_HANDLE_TYPE', 'This handle type cannot be sent', TypeError); E('ERR_INVALID_HTTP_TOKEN', '%s must be a valid HTTP token ["%s"]', TypeError, HideStackFramesError); E('ERR_INVALID_IP_ADDRESS', 'Invalid IP address: %s', TypeError); diff --git a/lib/internal/url.js b/lib/internal/url.js index 77f148144b391d..9105940b2a45a0 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1463,7 +1463,7 @@ function getPathFromURLWin32(url) { if ((pathname[n + 1] === '2' && third === 102) || // 2f 2F / (pathname[n + 1] === '5' && third === 99)) { // 5c 5C \ throw new ERR_INVALID_FILE_URL_PATH( - 'must not include encoded \\ or / characters', + 'must not include encoded \\ or / characters', url, ); } } @@ -1484,7 +1484,7 @@ function getPathFromURLWin32(url) { const sep = StringPrototypeCharAt(pathname, 2); if (letter < CHAR_LOWERCASE_A || letter > CHAR_LOWERCASE_Z || // a..z A..Z (sep !== ':')) { - throw new ERR_INVALID_FILE_URL_PATH('must be absolute'); + throw new ERR_INVALID_FILE_URL_PATH('must be absolute', url); } return StringPrototypeSlice(pathname, 1); } @@ -1551,7 +1551,7 @@ function getPathBufferFromURLWin32(url) { if (letter < CHAR_LOWERCASE_A || letter > CHAR_LOWERCASE_Z || // a..z A..Z (sep !== CHAR_COLON)) { - throw new ERR_INVALID_FILE_URL_PATH('must be absolute'); + throw new ERR_INVALID_FILE_URL_PATH('must be absolute', url); } // Now, we'll just return everything except the first byte of @@ -1570,6 +1570,7 @@ function getPathFromURLPosix(url) { if (pathname[n + 1] === '2' && third === 102) { throw new ERR_INVALID_FILE_URL_PATH( 'must not include encoded / characters', + url, ); } } diff --git a/test/parallel/test-url-fileurltopath.js b/test/parallel/test-url-fileurltopath.js index 338efacaa1a62c..e042e1aa6c222c 100644 --- a/test/parallel/test-url-fileurltopath.js +++ b/test/parallel/test-url-fileurltopath.js @@ -31,24 +31,6 @@ test('fileURLToPath with host', () => { } }); -test('fileURLToPath with invalid path', () => { - if (isWindows) { - assert.throws(() => url.fileURLToPath('file:///C:/a%2F/'), { - code: 'ERR_INVALID_FILE_URL_PATH' - }); - assert.throws(() => url.fileURLToPath('file:///C:/a%5C/'), { - code: 'ERR_INVALID_FILE_URL_PATH' - }); - assert.throws(() => url.fileURLToPath('file:///?:/'), { - code: 'ERR_INVALID_FILE_URL_PATH' - }); - } else { - assert.throws(() => url.fileURLToPath('file:///a%2F/'), { - code: 'ERR_INVALID_FILE_URL_PATH' - }); - } -}); - const windowsTestCases = [ // Lowercase ascii alpha { path: 'C:\\foo', fileURL: 'file:///C:/foo' }, diff --git a/test/parallel/test-url-invalid-file-url-path-input.js b/test/parallel/test-url-invalid-file-url-path-input.js new file mode 100644 index 00000000000000..f47df380d7eff3 --- /dev/null +++ b/test/parallel/test-url-invalid-file-url-path-input.js @@ -0,0 +1,25 @@ +'use strict'; + +// This tests that url.fileURLToPath() throws ERR_INVALID_FILE_URL_PATH +// for invalid file URL paths along with the input property. + +const { isWindows } = require('../common'); +const assert = require('assert'); +const url = require('url'); + +const inputs = []; + +if (isWindows) { + inputs.push('file:///C:/a%2F/', 'file:///C:/a%5C/', 'file:///?:/'); +} else { + inputs.push('file:///a%2F/'); +} + +for (const input of inputs) { + assert.throws(() => url.fileURLToPath(input), (err) => { + assert.strictEqual(err.code, 'ERR_INVALID_FILE_URL_PATH'); + assert(err.input instanceof URL); + assert.strictEqual(err.input.href, input); + return true; + }); +}