Error: ENOENT: no such file or directory when a file system operation targets a path that does not exist. The three most common causes are: (1) relative paths resolving from the wrong working directory — fix with path.join(__dirname, 'file'); (2) a missing intermediate directory — fix with fs.mkdirSync(dir, { recursive: true }); (3) using __dirname in an ES Module where it is undefined — fix with dirname(fileURLToPath(import.meta.url)).
What is ENOENT?
ENOENT stands for Error NO ENTry — a
POSIX system error code
meaning the named file or directory does not exist at the specified path. Node.js surfaces it whenever a
file system operation — readFile, writeFile, stat,
unlink, readdir, rename, lstat, scandir,
open, and others — cannot find the path you provided.
The error is thrown by the operating system's kernel and passed up through Node.js's
libuv layer. The integer value is errno: -2 on Linux/macOS and
errno: -4058 on Windows. Both map to the same code: 'ENOENT' string.
Error: ENOENT: no such file or directory, open '/project/config.json'Error: ENOENT: no such file or directory, scandir '/project/uploads'Error: ENOENT: no such file or directory, stat '/project/public/logo.png'Error: ENOENT: no such file or directory, rename '/tmp/old' -> '/tmp/new'Error: ENOENT: no such file or directory, lstat '/project/node_modules/.bin/tsc'
Full Error Example
Error: ENOENT: no such file or directory, open '/project/config.json'
at Object.openSync (node:fs:596:3)
at Object.readFileSync (node:fs:464:35)
at Object.<anonymous> (/project/app.js:3:19) {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/project/config.json'
}
The error object includes four diagnostic fields:
| Field | Value in example | Meaning |
|---|---|---|
code | 'ENOENT' | The error type — always ENOENT for missing file/directory |
syscall | 'open' | The fs operation that failed: open, stat, scandir, unlink, rename, lstat, etc. |
path | '/project/config.json' | The exact resolved path Node.js tried to access — always start debugging here |
errno | -2 | The numeric POSIX error code (-2 on Linux/macOS, -4058 on Windows) |
All Causes at a Glance
| Cause | Typical symptom | Fix |
|---|---|---|
| Typo in path string | conifg.json instead of config.json | Print and verify path.resolve() before using |
| Wrong working directory — relative path | Works when run from project root, fails in subdirectory | Use path.join(__dirname, 'file') |
| Intermediate directory missing | open 'output/reports/data.csv' when output/ absent | fs.mkdirSync(dir, { recursive: true }) |
| Async race condition | Works sometimes, fails intermittently | await the write before reading |
| Case-sensitivity mismatch on Linux | Works on macOS/Windows, fails on Linux CI | Match filename case exactly |
| Hardcoded absolute path | Works on one machine, fails on another or in Docker | Use __dirname or environment variables |
| Broken symbolic link | Path appears correct, lstat still throws ENOENT | fs.realpathSync() to detect, then recreate link |
ESM — __dirname undefined | ReferenceError: __dirname is not defined then ENOENT | Reconstruct from import.meta.url |
| File missing from Docker image | Works locally, fails in container | Add COPY instruction in Dockerfile |
| node_modules deleted or not installed | lstat '.../node_modules/.bin/...' | npm install or rm -rf node_modules && npm install |
| Windows path separator in string literal | Backslash path fails on Linux | Always use path.join() instead of string concatenation |
Cause 1 – Typo in the path
A single wrong character, missing extension, or incorrect directory name produces ENOENT. Always print the resolved path before assuming the file should exist.
const path = require('path');
const fs = require('fs');
const filePath = path.resolve('./conifg.json'); // typo: conifg
console.log('Looking for:', filePath); // always print to verify
const data = fs.readFileSync(filePath, 'utf8'); // ENOENT if path is wrong
Cause 2 – Wrong working directory
Relative paths are resolved against process.cwd() — the directory Node.js was
launched from, not the location of the source file. Running
node scripts/build.js from the project root resolves './config.json'
to /project/config.json. Running the same script from inside scripts/
resolves it to /project/scripts/config.json — a different path.
// Diagnose — always print both before hitting the error
console.log('cwd:', process.cwd());
console.log('resolved:', path.resolve('./config.json'));
// Fix: anchor to the source file's location with __dirname
const filePath = path.join(__dirname, 'config.json');
// __dirname is the directory of the current .js file, regardless of cwd
Cause 3 – Intermediate directory doesn't exist
Writing a file to ./output/reports/summary.csv fails with ENOENT if
output/ or output/reports/ do not exist yet. The
open syscall requires all parent directories to already exist.
Create the full directory tree before writing.
const fs = require('fs');
const path = require('path');
const outputDir = path.join(__dirname, 'output', 'reports');
// Creates all missing directories; safe to call even if they already exist
fs.mkdirSync(outputDir, { recursive: true });
fs.writeFileSync(path.join(outputDir, 'summary.csv'), data);
async function writeFileSafe(filePath, content) {
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
await fs.promises.writeFile(filePath, content);
}
Cause 4 – Async race condition
Reading a file immediately after triggering an async operation that creates it can fail
if the write hasn't finished yet. Always await the write before reading.
// Wrong – read may run before write completes
fs.writeFile('./data.json', content, () => {});
const result = fs.readFileSync('./data.json'); // ENOENT possible
// Correct – await the write first
await fs.promises.writeFile('./data.json', content);
const result = await fs.promises.readFile('./data.json', 'utf8');
Cause 5 – Case sensitivity on Linux
macOS and Windows are case-insensitive by default — Config.json and config.json
resolve to the same file. Linux (including most CI/CD and production servers) is case-sensitive.
Code that works locally can throw ENOENT in production or CI.
// Wrong on Linux (actual file is config.json)
const data = fs.readFileSync('./Config.json'); // ENOENT on Linux
// Correct – match the filename exactly
const data = fs.readFileSync('./config.json');
If you are unsure of the exact case, list the directory contents with
fs.readdirSync(dir) and compare programmatically, or use your terminal to
ls the directory and inspect every character.
Cause 6 – Hardcoded absolute paths
Absolute paths like /home/alice/project/data.csv or
C:\Users\alice\project\data.csv only work on one specific machine.
Use __dirname or environment variables to make paths portable across
machines, Docker containers, and CI environments.
// Wrong – breaks on every other machine or in Docker
const data = fs.readFileSync('/home/alice/project/data.csv');
// Correct – relative to the source file
const data = fs.readFileSync(path.join(__dirname, 'data.csv'));
// Also correct – configurable via environment variable
const dataPath = process.env.DATA_PATH || path.join(__dirname, 'data.csv');
const data = fs.readFileSync(dataPath);
Cause 7 – Broken symbolic link
A symbolic link whose target has been moved or deleted causes ENOENT even though
lstat can see the link entry itself. The error fires on stat,
readFile, or any operation that follows the link.
// Detect a broken symlink
try {
const realPath = fs.realpathSync(linkPath);
console.log('Resolved to:', realPath);
} catch (err) {
if (err.code === 'ENOENT') {
console.error('Broken symlink at:', linkPath);
// Option A: remove the broken link
fs.unlinkSync(linkPath);
// Option B: recreate it pointing to the correct target
fs.symlinkSync('/correct/target/path', linkPath);
}
}
To find all broken symlinks in a project tree, run in your terminal:
# Find broken symlinks (Linux/macOS)
find . -type l ! -exec test -e {} \; -print
Cause 8 – __dirname undefined in ES Modules
When using ES Modules (files with .mjs extension or "type": "module"
in package.json), the CommonJS globals __dirname and
__filename are not defined. Code that uses them will throw a
ReferenceError, and paths built without them often produce ENOENT.
// CommonJS (works in .cjs or without "type": "module")
const path = require('path');
const filePath = path.join(__dirname, 'config.json'); // works
// ES Module fix — reconstruct __dirname from import.meta.url
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { readFileSync } from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const filePath = join(__dirname, 'config.json'); // correct in ESM
const data = readFileSync(filePath, 'utf8');
Cause 9 – File missing from Docker image or CI workspace
Code that reads a configuration file, seed data, or template that exists locally but was never copied into the Docker image — or was gitignored and therefore absent from CI — will throw ENOENT in production or CI even though it works locally.
# Dockerfile — make sure to COPY the required files
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
# If config.json is required at runtime, copy it explicitly
COPY config.json ./
COPY src/ ./src/
CMD ["node", "src/index.js"]
In CI (GitHub Actions, GitLab CI, etc.), check that the file is not in .gitignore
and that any required secrets or config files are provided via environment variables or
CI secrets rather than committed files.
Cause 10 – npm / node_modules ENOENT
Running npx, npm run, or any script that executes a binary from
node_modules/.bin/ when node_modules is missing or partially
deleted will throw ENOENT with a path inside node_modules.
# Symptom
npm ERR! code ENOENT
npm ERR! syscall lstat
npm ERR! path /project/node_modules/.bin/webpack
# Fix 1 – install dependencies
npm install
# Fix 2 – full clean reinstall (fixes corrupted node_modules)
rm -rf node_modules package-lock.json
npm install
# Fix 3 – if using a monorepo, run install from the workspace root
cd /project-root && npm install
Cause 11 – Windows path separators
On Windows, paths use backslashes (\) as separators. String-concatenating
paths with hardcoded forward slashes or backslashes produces paths that fail on the
other OS. Always use path.join() which uses the correct separator for the
current platform.
// Wrong – fails on Windows because of forward slashes, or Linux because of backslashes
const filePath = __dirname + '/data/' + filename;
const filePath2 = __dirname + '\\data\\' + filename;
// Correct – path.join() uses the right separator for the current OS
const filePath = path.join(__dirname, 'data', filename);
Safe File Access Patterns
Check before reading (synchronous)
if (fs.existsSync(filePath)) {
const data = fs.readFileSync(filePath, 'utf8');
} else {
console.warn('File not found:', filePath);
// use a default value or throw a user-friendly error
}
Handle the error gracefully with try/catch (recommended)
try {
const data = await fs.promises.readFile(filePath, 'utf8');
return data;
} catch (err) {
if (err.code === 'ENOENT') {
// File doesn't exist — this is expected in some cases
console.error('File not found:', err.path);
return null; // or return a default
}
throw err; // re-throw unexpected errors (EACCES, EMFILE, etc.)
}
Async existence check with fs.access (no race condition)
// fs.access checks accessibility without opening the file
// Use F_OK to check existence, R_OK for read permission
try {
await fs.promises.access(filePath, fs.constants.F_OK);
const data = await fs.promises.readFile(filePath, 'utf8');
} catch (err) {
if (err.code === 'ENOENT') {
console.error('File does not exist:', filePath);
} else {
throw err;
}
}
Ensure directory exists before writing
async function writeFileSafe(filePath, content) {
// mkdir with recursive:true is a no-op if the directory already exists
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
await fs.promises.writeFile(filePath, content, 'utf8');
}
await writeFileSafe(path.join(__dirname, 'output', 'report.json'), JSON.stringify(data));
Reading a file with a fallback default
async function readJsonOrDefault(filePath, defaultValue) {
try {
const text = await fs.promises.readFile(filePath, 'utf8');
return JSON.parse(text);
} catch (err) {
if (err.code === 'ENOENT') return defaultValue;
throw err;
}
}
const config = await readJsonOrDefault(
path.join(__dirname, 'config.json'),
{ port: 3000, debug: false } // fallback if config.json doesn't exist
);
Debugging Checklist
- Read
err.pathfrom the error object — it shows the exact absolute path Node.js tried. - Print
process.cwd()to confirm the working directory at runtime. - Print
path.resolve('./your/path')to see the absolute path before using it. - Check
err.syscallto know which fs operation failed (open,stat,scandir,lstat, etc.). - Verify the file exists on disk:
ls -la /the/path/from/err.pathin your terminal. - Check if the case of every directory and filename matches exactly on disk.
- In Docker/CI, confirm the file is actually copied into the image/workspace and is not gitignored.
- If using ES Modules, ensure you have reconstructed
__dirnamefromimport.meta.url. - Check for broken symlinks with
find . -type l ! -exec test -e {} \; -print. - If the error path is inside
node_modules, runnpm installor a clean reinstall.
fs.existsSync() as a guard and then
calling fs.readFileSync() is still vulnerable to a race condition in concurrent
code — another process can delete the file between the two calls. Prefer
try/catch with err.code === 'ENOENT' for robust error handling.
Frequently Asked Questions
What is ENOENT in Node.js?
ENOENT stands for Error NO ENTry — a POSIX system error meaning the file or directory named in the path does not exist. Node.js surfaces it as Error: ENOENT: no such file or directory whenever a file system operation (readFile, writeFile, stat, unlink, readdir, rename, etc.) targets a path that cannot be found on disk.
What causes Error: ENOENT: no such file or directory?
The most common causes are: (1) relative path resolving from the wrong working directory — use path.join(__dirname, 'file') instead; (2) a typo in the path string; (3) a missing intermediate directory — create with fs.mkdirSync(dir, { recursive: true }); (4) async race condition; (5) case-sensitivity mismatch on Linux; (6) broken symbolic link; (7) __dirname used in an ES Module where it is not defined; (8) file missing from Docker image or CI workspace; (9) node_modules deleted or not installed.
How do I fix Error: ENOENT: no such file or directory?
Start by reading err.path from the error object — it shows the exact path Node.js tried. Then: (1) print process.cwd() to check the working directory; (2) replace relative paths with path.join(__dirname, 'filename'); (3) create missing directories with fs.mkdirSync(dir, { recursive: true }); (4) in ES Modules, reconstruct __dirname from import.meta.url; (5) verify the file actually exists on disk with the exact case shown in err.path.
How do I fix __dirname is not defined in ES Modules (ENOENT in .mjs)?
__dirname is a CommonJS global not available in ES Modules. Reconstruct it from import.meta.url:
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const filePath = join(__dirname, 'config.json');
How do I create a directory if it does not exist in Node.js?
Use fs.mkdirSync(path, { recursive: true }) (synchronous) or await fs.promises.mkdir(path, { recursive: true }) (async). The recursive option creates all intermediate directories and does not throw if the directory already exists. Always call this before writing a file to a potentially-missing directory.
How do I fix ENOENT: no such file or directory, scandir or lstat?
ENOENT on scandir or lstat means the directory itself does not exist. Check err.path to see which directory is missing. Create it with fs.mkdirSync(path, { recursive: true }) or guard with a try/catch:
try {
const entries = await fs.promises.readdir(dir);
} catch (err) {
if (err.code === 'ENOENT') {
console.warn('Directory does not exist:', dir);
} else throw err;
}
How do I fix npm ERR! code ENOENT during npm install?
npm ENOENT errors usually mean node_modules is corrupted, package.json is missing, or you are in the wrong directory. Fix by: (1) verifying you are in the directory containing package.json; (2) running npm install; (3) doing a full clean reinstall: rm -rf node_modules package-lock.json && npm install.
Why does ENOENT happen in production or CI but not locally?
Three common reasons: (1) case sensitivity — Linux CI/production is case-sensitive while macOS/Windows development machines are not; a filename like Config.json vs config.json works locally but fails on Linux; (2) missing file in Docker image — the file was not included in a COPY instruction in the Dockerfile; (3) gitignored file — a config or data file needed at runtime is in .gitignore and therefore absent from the CI workspace.
Related Errors
EACCES: permission denied— the path exists but the process lacks read/write permissionEMFILE: too many open files— too many file handles open simultaneously; the file exists but cannot be openedMODULE_NOT_FOUND— similar "not found" error from the module loader rather than the fs module; triggered byrequire()orimportERR_PACKAGE_PATH_NOT_EXPORTED— the package exists but the specific subpath is not exported inpackage.jsonexports mapENOTDIR: not a directory— a path component exists as a file, not a directoryEEXIST: file already exists— thrown by exclusive create operations when the path already exists