This error occurs when two or more modules require each other, creating a dependency loop. Node.js returns an incomplete module export to prevent infinite loops, resulting in undefined imports and runtime errors.
A circular dependency happens when moduleA requires moduleB, and moduleB requires moduleA, creating a loop where neither module can fully load. Node.js detects this cycle and returns an incompletely loaded version of the first module to break the infinite loop. This means that when moduleB tries to use exports from moduleA, those exports may be undefined because moduleA has not finished executing. The same issue can occur in chains like A → B → C → A. While Node.js handles this gracefully by not crashing, it leads to subtle bugs where imported values are undefined at runtime, causing ReferenceErrors or unexpected behavior.
Install and run Madge to visualize your module dependencies:
npm install -g madge
madge --circular --extensions js,ts ./srcFor a visual graph:
madge --circular --extensions js,ts --image graph.svg ./srcThis will identify all circular dependency chains in your codebase.
If two modules depend on each other for shared utilities, extract that code:
Before (circular):
// userService.js
const { validateOrder } = require('./orderService');
function getUser(id) { /* ... */ }
module.exports = { getUser };
// orderService.js
const { getUser } = require('./userService');
function validateOrder(order) {
const user = getUser(order.userId);
// ...
}
module.exports = { validateOrder };After (fixed):
// userService.js
function getUser(id) { /* ... */ }
module.exports = { getUser };
// orderService.js
const { getUser } = require('./userService');
function validateOrder(order) {
const user = getUser(order.userId);
// ...
}
module.exports = { validateOrder };
// If userService needs order validation, import orderService
// The dependency now flows in one direction onlyPass dependencies as function parameters rather than importing them:
Before:
// emailService.js
const { getUser } = require('./userService');
function sendEmail(userId) {
const user = getUser(userId);
// send email
}
// userService.js
const { sendEmail } = require('./emailService');
function createUser(data) {
// create user
sendEmail(user.id);
}After:
// emailService.js
function sendEmail(user) {
// send email
}
module.exports = { sendEmail };
// userService.js
function createUser(data, emailService) {
// create user
emailService.sendEmail(user);
}
module.exports = { createUser };
// app.js
const userService = require('./userService');
const emailService = require('./emailService');
userService.createUser(data, emailService);Move the require statement inside the function that uses it:
CommonJS:
// moduleA.js
function useModuleB() {
const moduleB = require('./moduleB'); // Lazy load
return moduleB.doSomething();
}
module.exports = { useModuleB };ES Modules:
// moduleA.js
export async function useModuleB() {
const moduleB = await import('./moduleB.js');
return moduleB.doSomething();
}This breaks the immediate circular dependency since moduleB is only loaded when the function executes.
Add the import/no-cycle rule to catch circular dependencies during development:
npm install --save-dev eslint-plugin-importIn .eslintrc.js:
module.exports = {
plugins: ['import'],
rules: {
'import/no-cycle': ['error', { maxDepth: 10 }],
},
};Or use Webpack's circular-dependency-plugin:
// webpack.config.js
const CircularDependencyPlugin = require('circular-dependency-plugin');
module.exports = {
plugins: [
new CircularDependencyPlugin({
exclude: /node_modules/,
failOnError: true,
}),
],
};CommonJS vs ES Modules:
ES modules (ESM) handle circular dependencies more gracefully than CommonJS. In ESM, bindings are set up before code execution, allowing circular references to work if you're not accessing values immediately during module initialization. However, both module systems can still have issues if you execute code that depends on circular imports during the module loading phase.
Barrel exports (index.js):
Be cautious with barrel exports that re-export from multiple files. They can inadvertently create circular dependencies:
// utils/index.js
export * from './userUtils';
export * from './orderUtils';
// If userUtils imports from orderUtils and vice versa,
// importing from utils/index creates a cycleTypeScript considerations:
TypeScript's type-only imports don't cause runtime circular dependencies but can still create compilation issues. Use import type when you only need types:
import type { User } from './userService';
// vs
import { User } from './userService';Architectural patterns:
Consider using the dependency inversion principle or event-driven architecture to avoid tight coupling. Instead of modules calling each other directly, use an event emitter or message bus to decouple communication.
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