{"record":{"id":"22b5946cff3c6b4a","repo":"vitest-dev/vitest","slug":"found-a-circular-projects-definition-file","errorCode":null,"errorMessage":"Found a circular \"projects\" definition: \"${file}\" -> \"${file}\". Make sure your configuration is correct.","messagePattern":"Found a circular \"projects\" definition: \"(.+?)\" -> \"(.+?)\"\\. Make sure your configuration is correct\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/node/projects/resolveProjects.ts","lineNumber":442,"sourceCode":"  for (const entry of entries) {\n    const definitions = entry.projectConfig.projects\n    // inline projects cannot declare `projects`; the declaring config's own\n    // entry (emitted when it references its own config file) is kept as-is —\n    // its `projects` are the definitions currently being resolved\n    if (entry.inline || definitions === undefined || entry.projectConfig === context.parentConfig) {\n      result.push(entry)\n      continue\n    }\n\n    const configFile = entry.viteConfig.configFile\n    const relativeFile = configFile\n      ? relative(context.rootConfig.root, configFile)\n      : entry.projectConfig.name\n    let chain = context.chain\n    if (configFile) {\n      const realConfigFile = safeRealpath(configFile)\n      if (chain.includes(realConfigFile)) {\n        throw new Error(\n          [\n            `Found a circular \"projects\" definition: `,\n            [...chain, realConfigFile].map(file => `\"${relative(context.rootConfig.root, file)}\"`).join(' -> '),\n            '. Make sure your configuration is correct.',\n          ].join(''),\n        )\n      }\n      chain = [...chain, realConfigFile]\n      context.containerConfigFiles.push(configFile)\n    }\n\n    const childContext: ProjectsResolutionContext = {\n      ...context,\n      parentViteConfig: entry.viteConfig,\n      parentConfig: entry.projectConfig,\n      ancestors: [...context.ancestors, entry.projectConfig.name],\n      chain,\n    }","sourceCodeStart":424,"sourceCodeEnd":460,"githubUrl":"https://github.com/vitest-dev/vitest/blob/1fa9837ec26533512fdcad8baebf249771bd340a/packages/vitest/src/node/projects/resolveProjects.ts#L424-L460","documentation":"When flattening container entries (config files that themselves declare 'projects'), Vitest tracks the chain of configFile paths (realpath-resolved). If a config file's realpath already appears in the current chain, resolution is cyclic and would loop forever, so it throws listing the full chain.","triggerScenarios":"Config file A declares projects including config file B, which (directly or transitively) declares projects including config file A again, forming a cycle in the realpath chain.","commonSituations":"Workspace files that reference each other, a project glob that re-includes the parent config, or a copy-pasted config that points back to its own container.","solutions":["Read the chain in the error to see which configs form the cycle.","Break the cycle by removing the back-reference in one of the configs.","Avoid globs that re-include a config file that already includes children.","Keep container/leaf roles separate: containers declare projects, leaves do not re-include containers."],"exampleFix":"// before - a.workspace.ts includes b.config.ts which includes a.workspace.ts\n// after - b.config.ts declares only leaf projects, no back-reference","handlingStrategy":"validation","validationCode":"// Detect cycles in a projects graph before resolution.\nfunction detectCycle(graph: Map<string, string[]>, start: string) {\n  const stack: string[] = []\n  const dfs = (node: string): boolean => {\n  if (stack.includes(node)) throw new Error(`Cycle: ${[...stack, node].join(' -> ')}`)\n  stack.push(node)\n  for (const next of graph.get(node) ?? []) if (dfs(next)) return true\n  stack.pop()\n  return false\n  }\n  dfs(start)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep container configs and leaf configs in separate roles; containers include, leaves do not re-include.","Avoid globs that re-match a parent config.","Review cross-references between workspace files."],"tags":["projects","config","circular","workspace"],"backgroundTag":null,"analyzedSha":"1fa9837ec26533512fdcad8baebf249771bd340a","analyzedAt":"2026-08-11T16:11:39.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}