fsPromises.rm()

Removes a file or directory, with recursive and force options mirroring the rm command.

Signature
fsPromises.rm(path[, options])
  • Added in Node.js v14.14.0

Parameters

ParameterTypeDescription
$pathstring | Buffer | URLFile or directory to remove.
$optionsoptionalObjectRemoval options.
$options.forceoptionalboolean= falseWhen true, exceptions will be ignored if path does not exist.
$options.recursiveoptionalboolean= falseIf true, perform a recursive directory removal. In recursive mode, operations are retried on failure.
$options.maxRetriesoptionalinteger= 0Number of retries after EBUSY, EMFILE, ENFILE, ENOTEMPTY or EPERM. Ignored unless recursive is true.
$options.retryDelayoptionalinteger= 100Milliseconds to wait between retries. Ignored unless recursive is true.

Return value

Promise<undefined>Fulfills with undefined upon success.

Errors

Rejects with ENOENT if the path does not exist and force is false, and with ERR_FS_EISDIR when the path is a directory and recursive is false.

Changelog

  • PHP v14.14.0Added as the replacement for fs.rmdir({ recursive: true }), which was deprecated for recursive removal.

fsPromises.rm() deletes a path. It is the modern replacement for the old unlink + rmdir split and for the deprecated rmdir(path, { recursive: true }): one function that handles files and trees.

The two options do different jobs

People conflate them because rm -rf pairs them on the command line, but they are independent. recursive: true means "descend into directories". force: true means "do not complain if the path is not there". Deleting a populated directory needs the first; making a cleanup step idempotent needs the second.

What trips people up

  • Calling rm() on a directory without recursive rejects with ERR_FS_EISDIR — not the ENOTEMPTY people expect from rmdir.
  • force: true only suppresses missing-path errors. Permission failures and busy files still reject.
  • On Windows, antivirus scanners and open handles cause intermittent EBUSY/EPERM. That is what maxRetries exists for, and it is ignored unless recursive is also true.
  • There is no undo and no trash. A wrong path built by string concatenation deletes real work.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Wipe a build or dist directory before a fresh build.
  • Clean up a temporary working folder in a finally block.
  • Delete a single uploaded file after processing it.
  • Make teardown idempotent with force: true so reruns do not fail.

Frequently asked questions

How do I do rm -rf in Node.js?

await fsPromises.rm(path, { recursive: true, force: true }) — recursive deletes directory contents and force ignores the path not existing.

Why does fsPromises.rm throw ERR_FS_EISDIR?

You pointed it at a directory without recursive: true. rm refuses to remove a directory unless you explicitly opt in to descending into it.

Should I use fs.rmdir or fs.rm to delete a folder?

Use fs.rm — recursive removal via fs.rmdir has been deprecated since Node 14.14.0 and emits a warning. fs.rmdir remains only for removing an already-empty directory.

More examples

force is about missing paths, not about directories

Prints force alone: ERR_FS_EISDIR, then no force: ENOENT, then the success line. It shows the two options solving two different problems — the first error is not something force can fix.

Example · javascript
import fs from 'node:fs/promises';

await fs.mkdir('stubborn', { recursive: true });
await fs.writeFile('stubborn/keep.txt', 'x');

try {
  await fs.rm('stubborn', { force: true });
} catch (err) {
  console.log('force alone:', err.code);
}

try {
  await fs.rm('missing-file');
} catch (err) {
  console.log('no force:', err.code);
}

await fs.rm('stubborn', { recursive: true, force: true });
console.log('both options worked');

Idempotent teardown in a finally block

Prints working in scratch-run then teardown is idempotent. force: true is what lets the same cleanup run after a failure that never created the directory, without a second try/catch.

Example · javascript
import fs from 'node:fs/promises';

const work = 'scratch-run';
try {
  await fs.mkdir(work, { recursive: true });
  await fs.writeFile(`${work}/partial.json`, '{"done":false}');
  console.log('working in', work);
} finally {
  await fs.rm(work, { recursive: true, force: true });
}

// Running the teardown a second time is still fine.
await fs.rm(work, { recursive: true, force: true });
console.log('teardown is idempotent');

Retrying a removal that keeps failing

Prints removed: true. maxRetries and retryDelay only take effect with recursive: true; they exist for Windows, where a virus scanner or lingering handle produces transient EBUSY and EPERM errors that succeed on a second attempt.

Example · javascript
import fs from 'node:fs/promises';

await fs.mkdir('busy/inner', { recursive: true });
await fs.writeFile('busy/inner/data.bin', 'x'.repeat(1024));

await fs.rm('busy', {
  recursive: true,
  force: true,
  maxRetries: 3,
  retryDelay: 50,
});

console.log('removed:', !(await fs.readdir('.')).includes('busy'));

Discussion

  • Be the first to comment on this lesson.