diff --git a/doc/api/errors.md b/doc/api/errors.md index 994c12c19c81b7..049f23cee95d5d 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1121,6 +1121,16 @@ added: An attempt to invoke an unsupported crypto operation was made. + + +### `ERR_CWD_DELETED` + + + +The current working directory has been deleted during the process' lifetime. + ### `ERR_DEBUGGER_ERROR` diff --git a/src/node_errors.h b/src/node_errors.h index 4d7132a9954505..76c2309292fd09 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -42,6 +42,7 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details); #define ERRORS_WITH_CODE(V) \ V(ERR_ACCESS_DENIED, Error) \ + V(ERR_CWD_DELETED, Error) \ V(ERR_BUFFER_CONTEXT_NOT_AVAILABLE, Error) \ V(ERR_BUFFER_OUT_OF_BOUNDS, RangeError) \ V(ERR_BUFFER_TOO_LARGE, Error) \ @@ -176,6 +177,10 @@ ERRORS_WITH_CODE(V) #define PREDEFINED_ERROR_MESSAGES(V) \ V(ERR_ACCESS_DENIED, "Access to this API has been restricted") \ + V(ERR_CWD_DELETED, \ + "Current working directory does not exist. " \ + "It may have been deleted while the process was still " \ + "inside it. Use process.chdir() to change to a valid directory.") \ V(ERR_BUFFER_CONTEXT_NOT_AVAILABLE, \ "Buffer is not available for the current Context") \ V(ERR_CLOSED_MESSAGE_PORT, "Cannot send data on closed MessagePort") \ diff --git a/src/node_process_methods.cc b/src/node_process_methods.cc index 9dd936c8ba9dcf..fd3f3040cfe7a9 100644 --- a/src/node_process_methods.cc +++ b/src/node_process_methods.cc @@ -162,6 +162,10 @@ static void Cwd(const FunctionCallbackInfo& args) { char buf[PATH_MAX_BYTES]; size_t cwd_len = sizeof(buf); int err = uv_cwd(buf, &cwd_len); + if (err == UV_ENOENT) { + THROW_ERR_CWD_DELETED(env); + return; + } if (err) { return env->ThrowUVException(err, "uv_cwd"); } diff --git a/test/parallel/test-cwd-enoent-preload.js b/test/parallel/test-cwd-enoent-preload.js index a7841e984d0eab..c6b542979001c6 100644 --- a/test/parallel/test-cwd-enoent-preload.js +++ b/test/parallel/test-cwd-enoent-preload.js @@ -30,6 +30,6 @@ proc.stdout.pipe(process.stdout); proc.stderr.pipe(process.stderr); proc.once('exit', common.mustCall(function(exitCode, signalCode) { - assert.strictEqual(exitCode, 0); + assert.strictEqual(exitCode, 1); assert.strictEqual(signalCode, null); })); diff --git a/test/parallel/test-process-cwd-deleted.js b/test/parallel/test-process-cwd-deleted.js new file mode 100644 index 00000000000000..f25dd06f6451d6 --- /dev/null +++ b/test/parallel/test-process-cwd-deleted.js @@ -0,0 +1,41 @@ +'use strict'; + +// This test verifies that an appropriate error is thrown when the current +// working directory is deleted and process.cwd() is called. +// +// We do this by spawning a forked process and deleting the tmp cwd +// while it is starting up. + +const common = require('../common'); +const { fork } = require('node:child_process'); +const { rmSync } = require('node:fs'); +const assert = require('node:assert'); + +if (process.argv[2] === 'child') { + while (true) { + process.cwd(); // Call process.cwd() to trigger failure + process.chdir('.'); // Reset the cache to force re-evaluation + } +} + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +const tmpDir = tmpdir.path; + +const proc = fork(__filename, ['child'], { + cwd: tmpDir, + silent: true, +}); + +let stderrData = ''; + +proc.stderr.on('data', (chunk) => { + stderrData += chunk.toString(); // Collect stderr output +}); + +proc.on('spawn', common.mustCall(() => rmSync(tmpDir, { recursive: true }))); + +proc.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 1); // Ensure process exits with error + assert.match(stderrData, /Current working directory does not exist/); +}));