This TypeScript error occurs when a project references itself in its own tsconfig.json file, creating a circular dependency. This typically happens in monorepos or project reference configurations where a project accidentally includes itself in its references array. The fix involves removing the self-reference from the references array.
The "Project cannot reference itself" error appears when TypeScript's project references feature detects a circular dependency where a project references itself. This is invalid because it would create an infinite compilation loop. TypeScript project references allow you to structure large codebases into smaller, interdependent projects that can be built independently. Each project has its own tsconfig.json file and can reference other projects. However, a project cannot reference itself because: 1. **Circular dependency**: A project building itself would need to wait for itself to finish building 2. **Infinite recursion**: The compiler would get stuck in an endless loop trying to resolve dependencies 3. **Build system confusion**: Tools like tsc --build wouldn't know where to start This error commonly occurs in: - Monorepos with many interconnected packages - Complex build configurations with nested project structures - Copy-paste errors when setting up project references - Automated tooling that incorrectly generates tsconfig.json files
Open your tsconfig.json file and examine the "references" array:
{
"compilerOptions": {
"composite": true,
"outDir": "./dist"
},
"references": [
{
"path": "../shared" // ✓ Correct - references another project
},
{
"path": "." // ✗ WRONG - references itself!
},
{
"path": "./" // ✗ WRONG - also references itself
}
]
}Remove any entries that point to the current directory (".", "./", or the project's own path).
In monorepos, ensure each package has clear boundaries:
// packages/app/tsconfig.json
{
"references": [
{
"path": "../shared" // ✓ References sibling package
},
{
"path": "../utils" // ✓ References another sibling
}
// NO self-reference here
]
}
// packages/shared/tsconfig.json
{
"references": [
// Can reference other packages, but not itself
{
"path": "../utils" // ✓ OK
}
]
}Use absolute or relative paths that clearly point to other projects, never to the current one.
For complex project structures, use helper functions or variables:
// Using tsconfig-paths or similar tools
{
"extends": "../../tsconfig.base.json",
"references": [
{
"path": "<workspaceRoot>/packages/shared"
},
{
"path": "<workspaceRoot>/packages/utils"
}
]
}
// Or with explicit relative paths:
{
"references": [
{
"path": "../../packages/shared" // Clear relative path
}
]
}Avoid dynamic path generation that might accidentally resolve to the current project.
Symlinks can create indirect self-references:
# Check for symlinks in your project
ls -la node_modules/
# If using npm/yarn workspaces, check for linked packages
npm ls --link
# Remove problematic symlinks if found
rm -f node_modules/current-packageAlso check package.json for unusual configurations:
{
"name": "my-package",
"dependencies": {
"my-package": "file:." // ✗ Self-reference in dependencies!
}
}Fix by removing self-dependencies.
Use TypeScript's build mode with verbose output to debug:
# Run build with verbose output
npx tsc --build --verbose
# Or for specific tsconfig
npx tsc --project tsconfig.json --build --verbose
# Output will show:
# Project 'packages/app' references:
# - packages/shared
# - packages/app <-- This is the problem!The verbose output clearly shows which projects reference which others, making self-references obvious.
After fixing the references, clean and rebuild:
# Clean build outputs
npx tsc --build --clean
# Or manually remove output directories
rm -rf dist
rm -rf build
rm -rf .tsbuildinfo
# Rebuild from scratch
npx tsc --build
# For incremental builds
npx tsc --build --incrementalIf using a monorepo tool:
# With npm workspaces
npm run build --workspaces
# With yarn workspaces
yarn workspaces foreach run build
# With pnpm
pnpm -r run build### Understanding TypeScript Project References
Project references are a TypeScript feature for structuring large codebases. Key concepts:
1. Composite Projects: Must have "composite": true in tsconfig.json
2. Reference Resolution: TypeScript follows references to build dependencies first
3. Build Outputs: Each project produces .tsbuildinfo files for incremental builds
### Common Monorepo Patterns
Lerna/Yarn Workspaces:
// packages/app/tsconfig.json
{
"references": [
{ "path": "../shared" },
{ "path": "../utils" }
]
}Nx Workspace:
{
"extends": "../../tsconfig.base.json",
"references": [
{ "path": "../shared" }
]
}Turborepo:
{
"references": [
{ "path": "../packages/shared" }
]
}### Debugging Complex Reference Chains
Use tsc --build --dry --verbose to see the build plan without executing:
npx tsc --build --dry --verboseThis shows the build order and reveals circular dependencies.
### Preventing Future Issues
1. Use tsconfig schema validation in IDEs
2. Add linting rules for tsconfig.json
3. Template tsconfig files for new projects
4. Automated validation in CI/CD pipelines
### Alternative: Solution References
For very complex dependency graphs, consider Solution References (tsconfig.solution.json):
// tsconfig.solution.json
{
"files": [],
"references": [
{ "path": "./packages/core" },
{ "path": "./packages/utils" },
{ "path": "./packages/app" }
]
}Then build with: tsc --build tsconfig.solution.json
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript