vitest-dev/vitest · error · Error
Found a circular "projects" definition: ${[...chain, realCon
Error message
Found a circular "projects" definition: ${[...chain, realConfigFile].map(file => `"${relative(context.rootConfig.root, file)}"`).join(' -> ')}. Make sure your configuration is correct. What it means
flattenContainerEntries (resolveProjects.ts:426-436) walks each container config (a file-based config that itself declares projects). It maintains a chain of realpath'd config files; if a container's file is already in the chain, the projects definitions form a cycle and Vitest throws, printing the cycle as file -> file -> file.
Source
Thrown at packages/vitest/src/node/projects/resolveProjects.ts:429
for (const entry of entries) {
const definitions = entry.projectConfig.projects
// inline projects cannot declare `projects`; the declaring config's own
// entry (emitted when it references its own config file) is kept as-is —
// its `projects` are the definitions currently being resolved
if (entry.inline || definitions === undefined || entry.projectConfig === context.parentConfig) {
result.push(entry)
continue
}
const configFile = entry.viteConfig.configFile
const relativeFile = configFile
? relative(context.rootConfig.root, configFile)
: entry.projectConfig.name
let chain = context.chain
if (configFile) {
const realConfigFile = safeRealpath(configFile)
if (chain.includes(realConfigFile)) {
throw new Error(
[
`Found a circular "projects" definition: `,
[...chain, realConfigFile].map(file => `"${relative(context.rootConfig.root, file)}"`).join(' -> '),
'. Make sure your configuration is correct.',
].join(''),
)
}
chain = [...chain, realConfigFile]
context.containerConfigFiles.push(configFile)
}
const childContext: ProjectsResolutionContext = {
...context,
parentViteConfig: entry.viteConfig,
parentConfig: entry.projectConfig,
ancestors: [...context.ancestors, entry.projectConfig.name],
chain,
}View on GitHub (pinned to d568f8ce37)
Solutions
- Inspect the printed cycle and break it by removing the back-reference.
- Use `extends` (single-parent inheritance) instead of mutual `projects` references for shared config.
- Centralize shared config in a base file that both projects extend, rather than referencing each other.
- If a config should also run as a project, list it once from the root and don't re-list it from a child.
Example fix
// before: a.config.ts projects: ['./b.config.ts']; b.config.ts projects: ['./a.config.ts']
// after: shared base, both extend it
// base.config.ts exports common test config
// a.config.ts: export default defineConfig([{ test: { extends: './base.config.ts', ... } }]) Defensive patterns
Strategy: validation
Validate before calling
function detectCycle(adjacency: Map<string, string[]>, start: string): string[] | null {
const seen = new Set<string>()
const stack: string[] = []
function visit(node: string): string[] | null {
if (stack.includes(node)) return [...stack.slice(stack.indexOf(node)), node]
if (seen.has(node)) return null
seen.add(node); stack.push(node)
for (const next of adjacency.get(node) ?? []) if (visit(next)) return visit(next)
stack.pop(); return null
}
return visit(start)
} Type guard
function hasCycle(files: string[], refs: Map<string, string[]>): boolean {
return detectCycle(refs, files[0]) !== null
} Prevention
- Use `extends` for one-way inheritance; reserve `projects` for true composition.
- Keep a single root that lists children; avoid children listing each other.
- Lint against a config file appearing in its own projects array.
When it happens
Trigger: Config A's projects array references config B, and config B's projects array references config A; a config lists itself in its own projects; a chain A -> B -> C -> A.
Common situations: Refactoring workspace files into projects arrays and accidentally creating a back-reference; two configs meant to extend each other via `extends` but wired through `projects` instead; copy-paste of a projects array that includes the source file.
Related errors
- No projects were found in "${relativeFile}". Make sure your
- Project name "${name}" is not unique. All projects should ha
- Project name "${name}"${entryFile ? ` from "${entryFile}"` :
- No projects were found. Make sure your configuration is corr
- Failed to initialize projects. There were errors during proj
AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03).
Data as JSON: /data/errors/9e0878301e18d1d7.json.
Report an issue: GitHub.