This error occurs when fs.mkdir() attempts to create a directory that already exists. It commonly happens in concurrent operations, race conditions, or when running build scripts multiple times without proper existence checks.
The EEXIST error is a filesystem error code that indicates an operation failed because a file or directory already exists at the target path. When you call fs.mkdir() or fs.mkdirSync() without the recursive option, Node.js will throw an EEXIST error if the directory already exists. This is intentional behavior designed to prevent accidentally overwriting existing directories. This error is particularly common in build scripts, test setups, CI/CD pipelines, and parallel processing scenarios where multiple processes or threads might attempt to create the same directory simultaneously. Understanding when and why this error occurs helps you implement proper directory creation patterns that handle existing directories gracefully.
The simplest and most recommended solution is to use { recursive: true } when calling fs.mkdir(). This option makes the operation idempotent - it won't throw an error if the directory already exists:
const fs = require('fs').promises;
// Recommended: Safe directory creation
async function ensureDir(dirPath) {
await fs.mkdir(dirPath, { recursive: true });
}
// Or with callback style
const fs = require('fs');
fs.mkdir('./output/logs', { recursive: true }, (err) => {
if (err) throw err;
console.log('Directory ready');
});The recursive: true option has two benefits: it creates parent directories if they don't exist (like mkdir -p), and it silently succeeds if the directory already exists. This is the standard approach for modern Node.js applications.
If you cannot use the recursive option or need different behavior for existing vs new directories, catch the EEXIST error specifically:
const fs = require('fs').promises;
async function createDir(dirPath) {
try {
await fs.mkdir(dirPath);
console.log('Directory created');
} catch (err) {
if (err.code === 'EEXIST') {
console.log('Directory already exists, continuing...');
} else {
throw err; // Re-throw other errors
}
}
}For synchronous code:
const fs = require('fs');
try {
fs.mkdirSync('./temp');
} catch (err) {
if (err.code !== 'EEXIST') throw err;
}This pattern is useful when you need to distinguish between "directory exists" and other filesystem errors like permission issues.
You can check if a directory exists before attempting to create it, though this approach has a small race condition window:
const fs = require('fs').promises;
async function ensureDirectory(dirPath) {
try {
await fs.access(dirPath);
// Directory exists
} catch {
// Directory doesn't exist, create it
await fs.mkdir(dirPath, { recursive: true });
}
}Using fs.existsSync() for synchronous code:
const fs = require('fs');
function ensureDirSync(dirPath) {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
}Note: The check-then-create pattern has a theoretical race condition in concurrent scenarios. The recursive: true approach (step 1) is more robust.
In environments with parallel processing (worker threads, child processes, or concurrent async operations), wrap mkdir calls with proper error handling:
const fs = require('fs').promises;
async function safeMkdir(dirPath) {
try {
await fs.mkdir(dirPath, { recursive: true });
} catch (err) {
// Verify it's a directory, not a file
const stat = await fs.stat(dirPath);
if (!stat.isDirectory()) {
throw new Error(`Path exists but is not a directory: ${dirPath}`);
}
// Otherwise, directory exists from another process - safe to continue
}
}This pattern is crucial in build tools, test frameworks, or any scenario where multiple processes share filesystem resources.
If your scripts create temporary directories, clean them up before or after runs to prevent EEXIST errors:
const fs = require('fs').promises;
const path = require('path');
async function setupBuildDir(buildPath) {
// Remove existing build directory if present
try {
await fs.rm(buildPath, { recursive: true, force: true });
} catch (err) {
// Ignore if directory doesn't exist
}
// Create fresh directory
await fs.mkdir(buildPath, { recursive: true });
}
// Or use a temp directory with automatic cleanup
const os = require('os');
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'myapp-'));For test frameworks like Jest, use beforeEach or afterEach hooks to manage temporary directories between tests.
Race conditions in concurrent environments: Even with proper error handling, there's a small window between checking if a directory exists and creating it. The { recursive: true } option eliminates this race condition entirely by making the operation atomic at the filesystem level.
Platform differences: On Windows, the error code may be reported differently in very old Node.js versions (errcode 47 instead of 17). Modern Node.js versions (10+) normalize this behavior across platforms.
File vs directory conflict: If a file (not a directory) exists at the target path, you'll get EEXIST when trying to create a directory there. Always verify with fs.stat() whether the existing entity is a file or directory before deciding how to proceed.
Build tool integration: Many build tools (webpack, Rollup, Vite) handle output directory creation automatically. If you encounter EEXIST errors with these tools, check if you're manually creating directories that the tool already manages.
Docker and CI/CD: In containerized environments, ensure your Dockerfile doesn't create directories that your application also tries to create. Use volume mounts for persistent directories and let the application create ephemeral ones.
Avoid Sync methods in production: While examples show both async and sync methods, avoid using mkdirSync() in server applications. It blocks the event loop and degrades performance. Use the async fs.promises API for production code.
Error: Listener already called (once event already fired)
EventEmitter listener already called with once()
Error: EACCES: permission denied, open '/root/file.txt'
EACCES: permission denied
Error: Invalid encoding specified (stream encoding not supported)
How to fix Invalid encoding error in Node.js readable streams
Error: EINVAL: invalid argument, open
EINVAL: invalid argument, open
TypeError: readableLength must be a positive integer (stream config)
TypeError: readableLength must be a positive integer in Node.js streams