Circular dependencies occur when two or more packages in an npm workspace import each other, creating a dependency loop that prevents proper module loading. This error is common in monorepos and can be detected and fixed by restructuring code or using detection tools.
A circular dependency exists when Package A requires Package B, and Package B requires Package A (directly or indirectly through other packages). When Node.js encounters this, it returns an incompletely loaded module to break the infinite loop, causing undefined imports and runtime errors. In npm workspaces, this is especially problematic because all packages are resolved together.
Install and run madge to visualize your dependency graph:
npm install --save-dev madge
npx madge --extensions js,ts,json --warn-on-cycle ./srcThis will output all circular dependencies found, showing the exact cycle path.
Create a diagram showing which packages depend on which:
{
"app": ["shared", "utils"],
"shared": ["utils", "app"],
"utils": []
}Identify which packages form a closed loop when you trace the dependencies.
Choose one of these restructuring strategies:
Option A: Extract common code to a third package
Before: app -> shared, shared -> app
After: app -> common, shared -> commonOption B: Merge closely coupled packages
If app and shared always work together, merge them into a single package.
If Package B only needs Package A for testing, move it to devDependencies:
{
"name": "package-b",
"dependencies": {},
"devDependencies": {
"package-a": "workspace:*"
}
}Dev dependencies are not imported at runtime, breaking the cycle.
After restructuring, reinstall:
rm -rf node_modules package-lock.json
npm cache clean --force
npm installRun madge again to confirm:
npx madge --extensions js,ts,json --warn-on-cycle ./srcIf no cycles are reported, the error is resolved.
Circular dependencies are sometimes considered necessary in complex software, but they should be avoided in npm workspaces whenever possible. Node.js does technically support them by returning a partially-loaded module, but this can lead to subtle bugs. For monorepos, Lerna provides a --reject-cycles flag that will prevent bootstrapping if cycles are detected. In NestJS applications, the forwardRef() utility can defer module resolution to work around circular dependencies in dependency injection.
npm ERR! code E401 npm ERR! 401 Unauthorized - Token has expired
Token has expired - npm authentication failure
npm ERR! code EAI_NODATA npm ERR! errno EAI_NODATA npm ERR! getaddrinfo EAI_NODATA registry.npmjs.org
How to fix "npm ERR! code EAI_NODATA - getaddrinfo EAI_NODATA"
npm ERR! code EMPTYPACKAGE npm ERR! Package contains no files
How to fix 'npm ERR! code EMPTYPACKAGE' - Package contains no files
npm ERR! code EWORKSPACEMISSING npm ERR! Workspace does not exist: packages/missing
How to fix "npm ERR! code EWORKSPACEMISSING - Workspace does not exist" error
npm ERR! code EADDRNOTAVAIL npm ERR! errno EADDRNOTAVAIL npm ERR! Address not available
How to fix "npm ERR! code EADDRNOTAVAIL - Address not available" error