Node.js Error

Error: ENOENT: no such file or directory

error code: ENOENT

Complete reference — what it means, every cause, and how to fix it.

Quick Answer: Node.js throws 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.

Exact error messages you will see:
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:

FieldValue in exampleMeaning
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-2The numeric POSIX error code (-2 on Linux/macOS, -4058 on Windows)

All Causes at a Glance

CauseTypical symptomFix
Typo in path stringconifg.json instead of config.jsonPrint and verify path.resolve() before using
Wrong working directory — relative pathWorks when run from project root, fails in subdirectoryUse path.join(__dirname, 'file')
Intermediate directory missingopen 'output/reports/data.csv' when output/ absentfs.mkdirSync(dir, { recursive: true })
Async race conditionWorks sometimes, fails intermittentlyawait the write before reading
Case-sensitivity mismatch on LinuxWorks on macOS/Windows, fails on Linux CIMatch filename case exactly
Hardcoded absolute pathWorks on one machine, fails on another or in DockerUse __dirname or environment variables
Broken symbolic linkPath appears correct, lstat still throws ENOENTfs.realpathSync() to detect, then recreate link
ESM — __dirname undefinedReferenceError: __dirname is not defined then ENOENTReconstruct from import.meta.url
File missing from Docker imageWorks locally, fails in containerAdd COPY instruction in Dockerfile
node_modules deleted or not installedlstat '.../node_modules/.bin/...'npm install or rm -rf node_modules && npm install
Windows path separator in string literalBackslash path fails on LinuxAlways 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);
Reusable helper: Wrap the pattern in a function so you never forget the directory step.
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

  1. Read err.path from the error object — it shows the exact absolute path Node.js tried.
  2. Print process.cwd() to confirm the working directory at runtime.
  3. Print path.resolve('./your/path') to see the absolute path before using it.
  4. Check err.syscall to know which fs operation failed (open, stat, scandir, lstat, etc.).
  5. Verify the file exists on disk: ls -la /the/path/from/err.path in your terminal.
  6. Check if the case of every directory and filename matches exactly on disk.
  7. In Docker/CI, confirm the file is actually copied into the image/workspace and is not gitignored.
  8. If using ES Modules, ensure you have reconstructed __dirname from import.meta.url.
  9. Check for broken symlinks with find . -type l ! -exec test -e {} \; -print.
  10. If the error path is inside node_modules, run npm install or a clean reinstall.
Common mistake: Using 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