Introduction

Node.js resolves require() paths relative to the current module file, not the process working directory. When a script is run from a different directory, or when the project structure changes, relative paths may resolve to non-existent locations. This causes MODULE_NOT_FOUND / ENOENT errors that work in development but fail in production or when run from cron.

Symptoms

  • Error: Cannot find module './config/database'
  • Error: ENOENT: no such file or directory, open './data/file.json'
  • Works when run from project root but fails from other directories
  • Works in development but fails in production deployment
  • Error after cd to a different directory before running the script

``` $ node src/server.js Error: Cannot find module './config/database' Require stack: - /home/user/project/src/server.js at Module._resolveFilename (node:internal/modules/cjs/loader:1075:15) at Module._load (node:internal/modules/cjs/loader:920:27) at Module.require (node:internal/modules/cjs/loader:1141:19)

# The file exists at /home/user/project/config/database.js # But require resolves relative to src/, not project root ```

Common Causes

  • require('./config') resolves relative to the calling file, not cwd
  • Running scripts from different working directory (cron, systemd)
  • Using ./ paths for non-module files (fs.readFile)
  • Project restructuring changing relative paths
  • ES modules with different resolution rules than CommonJS

Step-by-Step Fix

  1. 1.Use __dirname for file-relative paths:
  2. 2.```javascript
  3. 3.// WRONG - resolves relative to process.cwd(), not this file
  4. 4.const config = require('./config/database');
  5. 5.const data = fs.readFileSync('./data/file.json');

// CORRECT - resolves relative to this file's directory const path = require('path'); const config = require(path.join(__dirname, '../config/database')); const data = fs.readFileSync(path.join(__dirname, '../data/file.json')); ```

  1. 1.Use path.resolve for absolute paths:
  2. 2.```javascript
  3. 3.const path = require('path');

// Resolve from project root regardless of cwd const projectRoot = path.resolve(__dirname, '..'); const configPath = path.join(projectRoot, 'config', 'database.js'); const config = require(configPath);

// Or define a helper const root = path.resolve(__dirname, '..'); const resolveFromRoot = (...parts) => path.join(root, ...parts);

const config = require(resolveFromRoot('config', 'database')); const data = fs.readFileSync(resolveFromRoot('data', 'file.json'), 'utf-8'); ```

  1. 1.Set NODE_PATH for module resolution:
  2. 2.```bash
  3. 3.# Add project root to module search path
  4. 4.export NODE_PATH=/var/www/html
  5. 5.node src/server.js

# Now require resolves from project root // In src/server.js const config = require('config/database'); // Finds /var/www/html/config/database.js

// Or in package.json "scripts": { "start": "NODE_PATH=. node src/server.js" } ```

  1. 1.Handle ES modules with import.meta.url:
  2. 2.```javascript
  3. 3.// ESM does not have __dirname
  4. 4.import { fileURLToPath } from 'url';
  5. 5.import path from 'path';

const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename);

const configPath = path.join(__dirname, '../config/database.js'); const config = await import(configPath);

// Or use a helper import { resolve } from 'path'; const rootDir = resolve(__dirname, '..'); ```

  1. 1.Use module-alias for cleaner imports:
  2. 2.```bash
  3. 3.npm install module-alias
  4. 4.`
  5. 5.```javascript
  6. 6.// At the very top of your entry point
  7. 7.const moduleAlias = require('module-alias');
  8. 8.moduleAlias.addAlias('@config', __dirname + '/config');
  9. 9.moduleAlias.addAlias('@utils', __dirname + '/utils');

// Then anywhere in your code const config = require('@config/database'); const helper = require('@utils/format'); ```

Prevention

  • Always use __dirname or path.resolve() for file paths
  • Never use ./ for paths that need to work from different working directories
  • Add integration tests that run scripts from different directories
  • Use module-alias or similar for cleaner, absolute-style imports
  • In cron jobs, always cd to project root before running scripts:
  • ```cron
  • 0 2 * * * cd /var/www/html && node src/cron/cleanup.js
  • `
  • Use process.cwd() explicitly when you need the working directory
  • Document the expected working directory in your project README