fsPromises.stat()
Retrieves metadata for a path: size, timestamps, permissions and whether it is a file or directory.
fsPromises.stat(path[, options])- Added in Node.js v10.0.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$path | string | Buffer | URL | Path to inspect. Symbolic links are followed; use lstat() to inspect the link itself. |
$optionsoptional | Object | Stat options. |
$options.bigintoptional | boolean= false | Whether the numeric values in the returned fs.Stats object should be bigint. |
$options.signaloptional | AbortSignal= undefined | An AbortSignal to cancel the operation. |
Return value
Promise<fs.Stats>Fulfills with an fs.Stats object for the given path.
Errors
Rejects with ENOENT if the path does not exist, ELOOP on a symlink cycle, and EACCES when a parent directory is not searchable.
Changelog
- PHP v26.1.0Now accepts an additional signal property to allow aborting the operation.
- PHP v10.5.0Accepts an options object to request bigint numeric values, which is the only way to get nanosecond-precision timestamps.
fsPromises.stat() returns an fs.Stats object describing a path: size in bytes, mode, mtime/atime/ctime/birthtime as Date objects, plus the ms and Ns variants, and predicates such as isFile(), isDirectory() and isSymbolicLink().
When to reach for it
Use it when you need a fact about a file rather than its contents: is this big enough to reject, is it newer than the cached build output, is this entry a directory. Cache invalidation by mtimeMs is the classic case.
What trips people up
- It follows symlinks. Stat a link and you get the target's metadata, and
isSymbolicLink()is alwaysfalse. Uselstat()to see the link. - Do not use it as an existence check before opening a file — that is a TOCTOU race and an extra syscall. Just open it and handle
ENOENT. - Timestamp resolution is filesystem-dependent; two writes in the same millisecond can share an
mtime. Passbigint: truewhen you needmtimeNs. sizeon a directory is the directory entry's own size, not the total of its contents.
Example
When to use it
- Reject an upload that exceeds a size limit before reading it.
- Skip rebuilding an output file whose mtime is newer than its source.
- Distinguish files from directories while walking a tree.
- Report total bytes used by the files in a folder.
Frequently asked questions
How do I get a file's size in Node.js?
const { size } = await fsPromises.stat(path) — size is the length in bytes as a number, or a BigInt if you pass { bigint: true }.
How do I check whether a path is a directory?
(await fsPromises.stat(path)).isDirectory(). If you are already listing a folder, fsPromises.readdir with withFileTypes: true gives you the same answer without a second syscall.
Should I use stat to check if a file exists?
No. It costs an extra syscall and leaves a race window between the check and the open. Attempt the operation and handle ENOENT, or use fsPromises.access() when a check really is all you need.
More examples
Skip work when the output is newer than the source
Prints stale before edit: false, then stale after edit: true. mtimeMs is a plain number, which is why it is easier to compare than the mtime Date object.
import fs from 'node:fs/promises';
await fs.writeFile('page.md', '# hello');
await fs.writeFile('page.html', '<h1>hello</h1>');
async function isStale(src, out) {
const [a, b] = await Promise.all([fs.stat(src), fs.stat(out)]);
return a.mtimeMs > b.mtimeMs;
}
console.log('stale before edit:', await isStale('page.md', 'page.html'));
await fs.writeFile('page.md', '# hello again');
console.log('stale after edit:', await isStale('page.md', 'page.html'));
await fs.rm('page.md');
await fs.rm('page.html');Total the size of a directory's files
Prints the file list and total bytes: 350. Note that the directory's own stat().size would report the directory entry, not 350 — sizes only add up if you stat the files.
import fs from 'node:fs/promises';
import path from 'node:path';
await fs.mkdir('assets', { recursive: true });
await fs.writeFile('assets/a.txt', 'x'.repeat(100));
await fs.writeFile('assets/b.txt', 'y'.repeat(250));
const names = await fs.readdir('assets');
const sizes = await Promise.all(
names.map(async (n) => (await fs.stat(path.join('assets', n))).size)
);
console.log('files:', names.sort());
console.log('total bytes:', sizes.reduce((a, b) => a + b, 0));
await fs.rm('assets', { recursive: true });stat versus lstat on a symbolic link
stat reports true false 12 — the target's facts — while lstat reports false true. If you are building a directory walker, using stat here is how you end up in an infinite loop on a circular link.
import fs from 'node:fs/promises';
await fs.writeFile('real.txt', 'twelve chars');
await fs.symlink('real.txt', 'link.txt');
const viaStat = await fs.stat('link.txt');
const viaLstat = await fs.lstat('link.txt');
console.log('stat ->', viaStat.isFile(), viaStat.isSymbolicLink(), viaStat.size);
console.log('lstat ->', viaLstat.isFile(), viaLstat.isSymbolicLink());
await fs.rm('link.txt');
await fs.rm('real.txt');
Discussion