fs.stat()

Retrieves metadata about a path, such as its size, type and timestamps.

Signature
fs.stat(path[, options], callback)
  • Added in Node.js v0.0.2

Parameters

ParameterTypeDescription
$path<string> | <Buffer> | <URL>The path to inspect. Symlinks are followed, so the stats describe the target; use fs.lstat() to describe the link itself.
$optionsoptional<Object>= { bigint: false }bigint <boolean> (default false), which makes the numeric fields of the returned fs.Stats object BigInt, and throwIfNoEntry <boolean> (default true), added in v25.7.0, controlling whether a missing entry is an error or yields undefined.
$callback<Function>Called as (err, stats) where stats is an <fs.Stats> instance.

Return value

undefinedNothing. An fs.Stats object arrives as the callback's second argument.

Errors

Reports ENOENT for a missing path and EACCES when a parent directory is not searchable through the callback's err argument; in that case stats is undefined.

Changelog

  • PHP v25.7.0Accepts a throwIfNoEntry option controlling whether a missing entry raises an error or yields undefined.
  • PHP v18.0.0Passing an invalid callback now throws ERR_INVALID_ARG_TYPE instead of ERR_INVALID_CALLBACK.
  • PHP v10.5.0Accepts an options object with bigint, so sizes and inode numbers can be returned as BigInt.
  • 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.stat() answers the questions a path listing cannot: how large is this, is it a file or a directory, when was it last modified, what are its permission bits. The result is an fs.Stats object whose type predicates — isFile(), isDirectory(), isSymbolicLink() and friends — are methods, not properties. Writing stats.isFile without the parentheses gives you a function, which is always truthy; that is a classic silent bug.

stat, lstat and fstat

This one follows symlinks and describes the target, so a link to a file reports isFile() as true and isSymbolicLink() as false. Use fs.lstat() when you care about the link itself, and fs.fstat() when you already hold a file descriptor.

Numbers and timestamps

size is in bytes and is meaningful only for regular files — a directory's size is a filesystem bookkeeping figure, not the total of its contents. Timestamps come in two forms: mtime as a Date and mtimeMs as a number, plus nanosecond fields when bigint: true. Note that atime is frequently unreliable, since most systems mount with relatime or noatime and update it lazily or never.

Example

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

When to use it

  • Reject an upload that exceeds a size limit before processing its contents.
  • Decide whether a directory entry should be recursed into or read as a file.
  • Skip regenerating a cached artefact when its source has not changed since the last build.

Frequently asked questions

How do I get the size of a file in Node.js?

Call fs.stat() (or fs.statSync()) and read the size property of the returned fs.Stats object, which is the length in bytes. For files larger than 9 petabytes, pass { bigint: true } to keep the value exact.

What is the difference between fs.stat and fs.lstat?

fs.stat() follows symbolic links and describes the file they point to; fs.lstat() describes the link itself. Use lstat when walking a tree so that a symlink loop does not send you round forever.

Why is stats.isFile always true in my code?

You are almost certainly reading it as a property instead of calling it. isFile is a method, so stats.isFile is a function object that is always truthy &mdash; write stats.isFile() with the parentheses.

More examples

Distinguishing a directory from a file

The same call describes both kinds of entry, and only the regular file has a size that means anything. Note that the directory's own size field is deliberately not printed &mdash; it reflects filesystem bookkeeping, not the bytes of its contents.

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

fs.mkdirSync('bundle', { recursive: true });
fs.writeFileSync('bundle/app.js', 'x'.repeat(300));

fs.stat('bundle', (err, dirStats) => {
  if (err) throw err;
  console.log('bundle isDirectory:', dirStats.isDirectory());

  fs.stat('bundle/app.js', (err2, fileStats) => {
    if (err2) throw err2;
    console.log('app.js isFile:', fileStats.isFile());
    console.log('app.js size:', fileStats.size);
    fs.rmSync('bundle', { recursive: true, force: true });
  });
});

A missing path is an error, not empty stats

The callback receives ENOENT and stats is undefined. Anything that reads stats.size before checking err throws a TypeError that hides the real reason, which is why the err check comes first.

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

fs.stat('missing-file.txt', (err, stats) => {
  if (err) {
    console.log('code:', err.code);
    console.log('stats:', stats);
    return;
  }
  console.log(stats.size);
});

Exact sizes with bigint

With bigint: true every numeric field is a BigInt, so sizes beyond Number.MAX_SAFE_INTEGER stay exact. The trade-off is visible in the arithmetic: you must divide by 1024n, not 1024, and convert before mixing with ordinary numbers.

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

async function main() {
  await fsp.writeFile('big.bin', Buffer.alloc(5000));

  const stats = await fsp.stat('big.bin', { bigint: true });
  console.log('size type:', typeof stats.size);
  console.log('size:', stats.size.toString());
  console.log('kilobytes:', Number(stats.size / 1024n));

  await fsp.unlink('big.bin');
}

main();

Discussion

  • Be the first to comment on this lesson.