rimraf: Cross-Platform `rm -rf`
rimraf is a robust, cross-platform Node.js module that provides functionality akin to the `rm -rf` UNIX command for deep, recursive file and directory deletion. Currently at version 6.1.3, it is actively maintained with periodic major releases introducing significant changes, such as the shift from callbacks to Promises and the move away from default exports. A key differentiator is its specialized handling of common file system issues on Windows, such as `EBUSY` or `EMFILE` errors, through strategies like "move then remove" and exponential backoff, making it more reliable than simple native `fs.rm` alternatives in certain scenarios. It offers both synchronous and asynchronous APIs, as well as options to select native or JavaScript-based implementations. A critical aspect of its usage is the explicit warning against passing untrusted input due to its aggressive deletion capabilities, which can lead to system destruction or compromise if misused.
Common errors
-
TypeError: (0 , rimraf_1.default) is not a function
cause Attempting to use `rimraf` as a default import after version 5, where the default export was removed. This is common in TypeScript or Babel environments.fixUpdate your import statement to use named imports: `import { rimraf } from 'rimraf'`. -
Error: The last argument must be a callback function
cause Using the pre-v4 callback-based API with `rimraf` versions 4 or newer, which exclusively return Promises.fixRefactor your code to use the Promise-based API: `await rimraf(path)` or `rimraf(path).then(() => ...)`. Do not pass a callback as the last argument. -
Error: EBUSY: resource busy or locked, rmdir 'some-directory'
cause Attempting to delete a directory or file that is currently in use by another process, especially common on Windows. While `rimraf` has internal retries, persistent locks can still cause issues.fixEnsure no other processes (IDEs, terminals, antivirus) are accessing the target path. On Windows, consider configuring `maxRetries` or providing a `tmp` path. If possible, defer deletion until the resource is released. -
sh: 1: rimraf: not found
cause The `rimraf` CLI command is not found in the system's PATH, often when used in `package.json` scripts without `npm run` or `npx`, or if `rimraf` was not installed globally/locally.fixEnsure `rimraf` is installed as a `devDependency` (`npm install --save-dev rimraf`) and run it via `npm script` (e.g., `npm run clean`) or `npx rimraf`. For global use, install with `npm install -g rimraf` and verify `npm`'s global bin directory is in your PATH. -
Paths with glob patterns are not being deleted as expected.
cause Globbing functionality is no longer implicitly enabled in `rimraf` v4+ and requires explicit opt-in.fixAdd the `{ glob: true }` option to your `rimraf` call: `await rimraf('path/*.txt', { glob: true })`. If using the CLI, add the `--glob` flag.
Warnings
- breaking Node.js runtime requirement was updated to `20 || >=22` in v6.0.0. Older Node.js versions are no longer supported.
- breaking The default export was removed in v5.0.0. Direct named imports are now required for all functions like `rimraf` and `rimrafSync`.
- breaking Starting with v4.0.0, `rimraf` functions return a `Promise` instead of accepting a callback. This significantly changes the asynchronous API usage.
- breaking In v4.0.0, globbing functionality was initially removed. It was reintroduced as an opt-in feature in v4.2.0, requiring the `--glob` CLI option or `glob: true` option property to be set for glob patterns to be processed.
- gotcha NEVER pass untrusted user input directly to `rimraf` or its CLI tool. This utility aggressively deletes files and directories, and malicious input can lead to irreversible data loss, system destruction, or compromise. The `tmp` option for `--impl=move-remove` can also be exploited to move files to arbitrary locations.
- gotcha On Windows, `rimraf` employs specific strategies (like 'move then remove' and exponential backoff) to handle `EBUSY`, `EMFILE`, and `ENFILE` errors. You can configure these retries and backoff parameters via the `maxRetries`, `backoff`, and `maxBackoff` options, and specify a `tmp` directory.
Install
-
npm install rimraf -
yarn add rimraf -
pnpm add rimraf
Imports
- rimraf
import rimraf from 'rimraf'
import { rimraf } from 'rimraf' - rimrafSync
const rimrafSync = require('rimraf').rimrafSyncimport { rimrafSync } from 'rimraf' - native
import { rimraf } from 'rimraf/native'import { native } from 'rimraf'
Quickstart
import { rimraf } from 'rimraf';
import { mkdir, writeFile, stat } from 'node:fs/promises';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
async function exampleUsage() {
const tempDirPath = join(tmpdir(), `rimraf-example-${Date.now()}`);
const tempFilePath = join(tempDirPath, 'test-file.txt');
console.log(`Creating temporary directory: ${tempDirPath}`);
await mkdir(tempDirPath, { recursive: true });
await writeFile(tempFilePath, 'Hello, rimraf!');
console.log(`Created temporary file: ${tempFilePath}`);
try {
const stats = await stat(tempDirPath);
console.log(`Directory exists before deletion: ${stats.isDirectory()}`);
} catch (err) {
console.error(`Error checking directory before deletion: ${err}`);
}
console.log(`Attempting to delete: ${tempDirPath}`);
// The 'rimraf' function returns a boolean indicating successful removal
const success = await rimraf(tempDirPath);
if (success) {
console.log(`Successfully deleted: ${tempDirPath}`);
try {
await stat(tempDirPath);
console.error('ERROR: Directory still exists after deletion!');
} catch (err: any) {
if (err.code === 'ENOENT') {
console.log('Directory no longer exists (as expected).');
} else {
console.error(`Error checking directory after deletion: ${err}`);
}
}
} else {
console.error(`Failed to delete: ${tempDirPath}. (A filter might have prevented full removal.)`);
}
}
exampleUsage().catch(console.error);