How to fix npm ERR! code ECYCLE npm ERR! Circular dependency detected in workspaces in npm
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.
Quick fix
Fixes ECYCLE
npm install --save-dev madge
npx madge --extensions js,ts,json --warn-on-cycle ./src- Detect circular dependencies with madge
- Map out the dependency chain
- Break the cycle by restructuring
- Separate dev and runtime dependencies
npm install --save-dev madgenpx madge --extensions js,ts,json --warn-on-cycle ./srcnpm ERR! code ECYCLEnpm ERR! Circular dependency detected in workspaces
What this error means
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.
Typical symptoms
- Build fails with 'ECYCLE' error code when running npm install
- Runtime errors showing 'undefined is not a function' for imports from workspace packages
- Only certain dependency orders cause the error to appear or disappear
- Error message includes a cycle path like 'package-a -> package-b -> package-a'
- Package works fine in isolation but fails when used with workspaces
Common causes
- Package A has a direct dependency on Package B, and Package B has a direct runtime dependency on Package A
- Indirect circular reference through multiple packages (A -> B -> C -> A)
- Package has a runtime dependency on another package, which has a dev-only dependency back to the first
- Shared utilities module is imported by multiple packages that also import each other
- Refactoring created accidental cross-imports between previously independent packages
How to fix "npm ERR! code ECYCLE npm ERR! Circular dependency detected in workspaces"
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.
Advanced notes
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.
Frequently asked questions
- What does "ECYCLE" mean?
- A circular dependency exists when Package A requires Package B, and Package B requires Package A (directly or indirectly through other packages). 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.
- How do I fix "ECYCLE"?
- 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.
- What causes "ECYCLE"?
- Package A has a direct dependency on Package B, and Package B has a direct runtime dependency on Package A Indirect circular reference through multiple packages (A -> B -> C -> A) Package has a runtime dependency on another package, which has a dev-only dependency back to the first
- Is the "ECYCLE" error serious?
- Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.
Official resources & further reading
- ๐npm Official Documentation
- ๐npm Docs: npm Workspaces(Official Docs)
- ๐ฌStack Overflow: npm Workspaces(Stack Overflow)
- ๐npm GitHub Issues(GitHub)