fs.unlink()

Deletes a file or symbolic link asynchronously, failing on directories.

Signature
fs.unlink(path, callback)
  • Added in Node.js v0.0.2

Parameters

ParameterTypeDescription
$path<string> | <Buffer> | <URL>The file or symbolic link to remove. A symlink is removed itself, never its target. Directories are not accepted.
$callback<Function>Called as (err), null on success.

Return value

undefinedNothing. Success or failure is reported through the callback's err argument.

Errors

Reports ENOENT when the file does not exist, EACCES or EPERM on permission failures, and an error on directories (EISDIR on Linux, EPERM on macOS), all through the callback's err argument.

Changelog

  • PHP v18.0.0Passing an invalid callback now throws ERR_INVALID_ARG_TYPE instead of ERR_INVALID_CALLBACK.
  • PHP v10.0.0The callback parameter is no longer optional; omitting it throws a TypeError at runtime.
  • PHP v7.6.0path may be a WHATWG URL object using the file: protocol.

fs.unlink() removes a name from the filesystem. That wording matters: it deletes the directory entry, and the file's data is reclaimed only once the last name pointing to it is gone and no process still holds it open. A file you unlink while a stream is reading it stays fully readable until that stream closes — the disk space simply is not freed yet.

Files and links only

Passing a directory fails (EISDIR on Linux, EPERM on macOS). Use fs.rm() or fs.rmdir() for those. Given a symlink, unlink deletes the link and leaves the target untouched, which is what you almost always want.

Deleting something that is not there

That is ENOENT, not a silent success. Cleanup code that runs twice, or runs after a failed setup, needs to tolerate it — either ignore err.code === 'ENOENT' or use fs.rm(path, { force: true }), which suppresses it for you.

Also note that permission to delete comes from the write bit on the containing directory, not on the file. A read-only file in a writable directory deletes fine; a writable file in a read-only directory does not.

Example

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

When to use it

  • Remove a temporary upload after it has been processed and stored elsewhere.
  • Delete a stale lock file or PID file during shutdown.
  • Prune expired entries from a cache directory one file at a time.

Frequently asked questions

How do I delete a file in Node.js?

Call fs.unlink(path, callback), or await fs.promises.unlink(path). For a path that might be a directory, or when a missing file should not be an error, use fs.rm() instead.

Why does fs.unlink fail on a directory?

Because unlink removes only files and symbolic links; directories need fs.rm() with recursive: true, or fs.rmdir() when they are already empty. The error is EISDIR on Linux and EPERM on macOS.

Does fs.unlink free the disk space immediately?

Only if no process still has the file open. Unlink removes the directory entry; the data is reclaimed when the last open handle closes, which is why deleting a large log file being written to does not recover space until the writer restarts.

More examples

Deleting a file that is not there

The missing file gives ENOENT rather than succeeding quietly, while the real file deletes with a null error. Idempotent cleanup therefore has to ignore ENOENT explicitly, or switch to fs.rm(path, { force: true }).

Example · javascript
const fs = require('node:fs');

fs.unlink('already-gone.txt', (err) => {
  console.log('first attempt:', err && err.code);

  fs.writeFileSync('already-gone.txt', 'now it exists');
  fs.unlink('already-gone.txt', (err2) => {
    console.log('second attempt error:', err2);
    console.log('exists:', fs.existsSync('already-gone.txt'));
  });
});

Directories are not accepted

The unlink fails even though the directory is empty; the exact code differs by platform, which is why the example checks that an error occurred rather than matching a string. fs.rm handles the directory case.

Example · javascript
const fs = require('node:fs');

fs.mkdirSync('a-directory', { recursive: true });

fs.unlink('a-directory', (err) => {
  console.log('unlink on a directory failed:', err !== null);
  console.log('error is an Error:', err instanceof Error);

  fs.rm('a-directory', { recursive: true }, (err2) => {
    if (err2) throw err2;
    console.log('fs.rm removed it:', !fs.existsSync('a-directory'));
  });
});

Deleting many files in parallel

Only the two .tmp files are removed and keep.txt survives. Promise.all issues the deletions concurrently, which is safe here because each targets a distinct path; running unbounded parallel unlinks over tens of thousands of files can exhaust file descriptors, so batch them in that case.

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

async function main() {
  fs.mkdirSync('tmpfiles', { recursive: true });
  for (const name of ['a.tmp', 'b.tmp', 'keep.txt']) {
    fs.writeFileSync(`tmpfiles/${name}`, '');
  }

  const files = fs.readdirSync('tmpfiles').sort();
  console.log('before:', files);

  await Promise.all(
    files.filter((f) => f.endsWith('.tmp')).map((f) => fsp.unlink(`tmpfiles/${f}`))
  );

  console.log('after:', fs.readdirSync('tmpfiles').sort());
  fs.rmSync('tmpfiles', { recursive: true, force: true });
}

main();

Discussion

  • Be the first to comment on this lesson.