This error occurs when attempting to remove a directory that contains files or subdirectories using fs.rmdir() or fs.rmdirSync(). Node.js refuses to delete non-empty directories unless the recursive option is enabled.
The ENOTEMPTY error is a filesystem error that appears when you try to delete a directory that still contains files or subdirectories. The error code "ENOTEMPTY" literally means "Error: Not Empty." By default, Node.js's fs.rmdir() and fs.rmdirSync() methods only delete empty directories as a safety measure. This prevents accidental deletion of important data. When the directory contains any content, Node.js throws this error instead of proceeding with the deletion. This error commonly appears in build scripts, cleanup operations, deployment pipelines, and any code that manages temporary directories. It can also occur during npm package installations when npm tries to replace directories with files but finds existing content that wasn't properly cleaned up from previous installations.
Before attempting to delete a directory, check what it contains:
const fs = require('fs');
// Check if directory exists and what it contains
if (fs.existsSync('./dirname')) {
const contents = fs.readdirSync('./dirname');
console.log('Directory contains:', contents);
}This helps you understand what's preventing the deletion and whether the directory should actually be removed.
The modern approach is to use fs.rm() with the recursive and force options:
const fs = require('fs');
// Callback-based
fs.rm('./dirname', { recursive: true, force: true }, (err) => {
if (err) {
console.error('Error removing directory:', err);
return;
}
console.log('Directory removed successfully');
});
// Promise-based (recommended)
const fsPromises = require('fs/promises');
async function removeDirectory() {
try {
await fsPromises.rm('./dirname', { recursive: true, force: true });
console.log('Directory removed successfully');
} catch (err) {
console.error('Error removing directory:', err);
}
}The force: true option makes it ignore errors if the directory doesn't exist, similar to rm -rf on Unix systems.
If you're using Node.js 12.10.0 or later but before 14.14.0, use fs.rmdir() with the recursive option:
const fs = require('fs');
fs.rmdir('./dirname', { recursive: true }, (err) => {
if (err) {
console.error('Error removing directory:', err);
return;
}
console.log('Directory removed successfully');
});
// Promise-based
const fsPromises = require('fs/promises');
async function removeDirectory() {
try {
await fsPromises.rmdir('./dirname', { recursive: true });
console.log('Directory removed successfully');
} catch (err) {
console.error('Error removing directory:', err);
}
}Note: fs.rmdir() with recursive option is deprecated in favor of fs.rm().
If you encounter ENOTEMPTY errors due to concurrent operations, add retry logic with maxRetries:
const fsPromises = require('fs/promises');
async function removeDirectoryWithRetry() {
try {
await fsPromises.rm('./dirname', {
recursive: true,
force: true,
maxRetries: 3,
retryDelay: 100, // milliseconds
});
console.log('Directory removed successfully');
} catch (err) {
console.error('Failed to remove directory after retries:', err);
}
}This handles transient issues like files being temporarily locked by other processes.
Ensure no processes have the directory or its files open:
const fsPromises = require('fs/promises');
async function safeRemoveDirectory() {
// Close any open file handles first
// Example: if you have an open stream
if (fileStream) {
await new Promise((resolve) => fileStream.close(resolve));
}
// Wait a moment for OS to release handles
await new Promise((resolve) => setTimeout(resolve, 100));
// Now remove the directory
await fsPromises.rm('./dirname', { recursive: true, force: true });
}Close file manager windows, IDEs, terminals, or any other programs that might be accessing the directory.
Deprecation Notice: The fs.rmdir() method with the recursive option is deprecated since Node.js 14.14.0. Always use fs.rm() for recursive directory deletion in modern Node.js versions.
Platform Differences: On Windows, directories containing read-only files may require the force: true option even with recursive: true. On Unix-like systems, permission errors (EACCES or EPERM) may occur if you lack write permissions on the parent directory.
npm ENOTEMPTY Errors: If you encounter this during npm install, the issue is usually in node_modules management. Try: (1) delete node_modules and package-lock.json, (2) clear npm cache with npm cache clean --force, (3) reinstall dependencies with npm install. This often resolves corruption from interrupted installations.
Performance Considerations: Recursive directory deletion can be slow for large directory trees. For build systems handling many files, consider using specialized packages like rimraf or del which optimize recursive deletion and provide cross-platform consistency.
Atomic Operations: Directory deletion is not atomic. If your application crashes during a recursive delete, the directory may be left in a partially-deleted state. For critical operations, consider using a two-phase approach: rename the directory first (which is atomic), then delete the renamed directory in the background.
maxRetries Behavior: When using maxRetries with fs.rm(), Node.js uses linear backoff, waiting retryDelay * attemptNumber milliseconds between attempts. This is particularly useful for handling EBUSY, EMFILE, ENFILE, ENOTEMPTY, and EPERM errors that may be transient.
Error: Listener already called (once event already fired)
EventEmitter listener already called with once()
Error: EACCES: permission denied, open '/root/file.txt'
EACCES: permission denied
Error: Invalid encoding specified (stream encoding not supported)
How to fix Invalid encoding error in Node.js readable streams
Error: EINVAL: invalid argument, open
EINVAL: invalid argument, open
TypeError: readableLength must be a positive integer (stream config)
TypeError: readableLength must be a positive integer in Node.js streams