How to fix Error: Module A requires Module B which requires Module A (circular dependency) in Node.js

Node.jsINTERMEDIATEMEDIUMUpdated Dec 9, 2025· 204 views

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.

Quick fix

Fixes Error: Module A requires Module B which requires Module A (circular dependency)

npm install -g madge
madge --circular --extensions js,ts ./src
  1. Detect circular dependencies using Madge
  2. Extract shared logic into a separate module
  3. Use dependency injection instead of direct imports
  4. Delay imports using dynamic require() or import()
node.js — error
npm install -g madge
madge --circular --extensions js,ts ./src
Error: Module A requires Module B which requires Module A (circular dependency)

What this error means

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.

Typical symptoms

  • Imported functions or variables are undefined at runtime
  • ReferenceError: Cannot access variable before initialization
  • Module exports appear as empty objects {}
  • Unexpected behavior where code works in one order but fails in another
  • ESLint warnings about import/no-cycle if configured
  • Application crashes with "X is not a function" errors

Common causes

  1. Two modules directly import each other at the top level
  2. Chain of imports creates a cycle (A imports B, B imports C, C imports A)
  3. Shared types or utilities are scattered across mutually dependent modules
  4. Poor separation of concerns leading to tightly coupled modules
  5. Attempting to split large files without proper dependency planning
  6. Using barrel exports (index.js files) that re-export mutually dependent modules

How to fix "Error: Module A requires Module B which requires Module A (circular dependency)"

1Detect circular dependencies using Madge

Install and run Madge to visualize your module dependencies:

bash
npm install -g madge
madge --circular --extensions js,ts ./src

For a visual graph:

bash
madge --circular --extensions js,ts --image graph.svg ./src

This will identify all circular dependency chains in your codebase.

2Extract shared logic into a separate module

If two modules depend on each other for shared utilities, extract that code:

Before (circular):

javascript
// 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):

javascript
// 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 only
3Use dependency injection instead of direct imports

Pass dependencies as function parameters rather than importing them:

Before:

javascript
// 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:

javascript
// 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);
4Delay imports using dynamic require() or import()

Move the require statement inside the function that uses it:

CommonJS:

javascript
// moduleA.js
function useModuleB() {
  const moduleB = require('./moduleB'); // Lazy load
  return moduleB.doSomething();
}
module.exports = { useModuleB };

ES Modules:

javascript
// 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.

5Prevent future circular dependencies with ESLint

Add the import/no-cycle rule to catch circular dependencies during development:

bash
npm install --save-dev eslint-plugin-import

In .eslintrc.js:

javascript
module.exports = {
  plugins: ['import'],
  rules: {
    'import/no-cycle': ['error', { maxDepth: 10 }],
  },
};

Or use Webpack's circular-dependency-plugin:

javascript
// webpack.config.js
const CircularDependencyPlugin = require('circular-dependency-plugin');

module.exports = {
  plugins: [
    new CircularDependencyPlugin({
      exclude: /node_modules/,
      failOnError: true,
    }),
  ],
};

Advanced notes

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:

javascript
// utils/index.js
export * from './userUtils';
export * from './orderUtils';

// If userUtils imports from orderUtils and vice versa,
// importing from utils/index creates a cycle

TypeScript 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:

typescript
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.

Frequently asked questions

What does "Error: Module A requires Module B which requires Module A (circular de" mean?
A circular dependency happens when moduleA requires moduleB, and moduleB requires moduleA, creating a loop where neither module can fully load. 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.
How do I fix "Error: Module A requires Module B which requires Module A (circular de"?
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.
What causes "Error: Module A requires Module B which requires Module A (circular de"?
Two modules directly import each other at the top level Chain of imports creates a cycle (A imports B, B imports C, C imports A) Shared types or utilities are scattered across mutually dependent modules
Is the "Error: Module A requires Module B which requires Module A (circular de" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading