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/);
+}));