{"id":"a0c5a7444585455e","repo":"vitest-dev/vitest","slug":"circular-fixture-dependency-detected-fixture-na","errorCode":null,"errorMessage":"Circular fixture dependency detected: ${fixture.name} <- ${[...depSet].reverse().map(d => d.name).join(' <- ')}","messagePattern":"Circular fixture dependency detected: (.+?) <- (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/runtime/runner/fixture.ts","lineNumber":573,"sourceCode":"  usedFixtures: TestFixtureItem[],\n  registrations: FixtureRegistrations,\n  depSet = new Set<TestFixtureItem>(),\n  pendingFixtures: TestFixtureItem[] = [],\n) {\n  usedFixtures.forEach((fixture) => {\n    if (pendingFixtures.includes(fixture)) {\n      return\n    }\n    if (!isFixtureFunction(fixture.value) || !fixture.deps) {\n      pendingFixtures.push(fixture)\n      return\n    }\n    if (depSet.has(fixture)) {\n      if (fixture.parent) {\n        fixture = fixture.parent\n      }\n      else {\n        throw new Error(\n          `Circular fixture dependency detected: ${fixture.name} <- ${[...depSet]\n            .reverse()\n            .map(d => d.name)\n            .join(' <- ')}`,\n        )\n      }\n    }\n\n    depSet.add(fixture)\n    resolveDeps(\n      Array.from(fixture.deps, n => n === fixture.name ? fixture.parent : registrations.get(n)).filter(n => !!n),\n      registrations,\n      depSet,\n      pendingFixtures,\n    )\n    pendingFixtures.push(fixture)\n    depSet.clear()\n  })","sourceCodeStart":555,"sourceCodeEnd":591,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/vitest/src/runtime/runner/fixture.ts#L555-L591","documentation":"Vitest resolves fixture dependencies by walking the dependency graph in `resolveDeps` (fixture.ts:554). If fixture A depends on B which depends back on A (directly or transitively) and there is no `parent` base implementation to break the cycle, the resolver detects the revisit and throws this error with the full cycle chain formatted as `A <- B <- A`.","triggerScenarios":"Defining `a: ({ b }, use) => use(...)` and `b: ({ a }, use) => use(...)`; any transitive cycle across three or more fixtures; overriding a fixture to depend on one that ultimately depends on the override target.","commonSituations":"Splitting a monolithic fixture into two that reference each other; overriding fixtures in nested suites creating a cycle; refactoring fixtures without checking the dependency direction.","solutions":["Break the cycle by introducing a shared base fixture that both depend on instead of each other.","Use `test.extend` parent/base implementation: redefining a fixture by the same name creates a `parent` that breaks the self-dependency.","Restructure so dependencies flow one direction (leaf fixtures have no fixture deps)."],"exampleFix":"// before: a depends on b, b depends on a\ntest.extend({\n  a: ({ b }, use) => use(b + 1),\n  b: ({ a }, use) => use(a + 1),\n})\n// after: shared base\ntest.extend({\n  base: ({}, use) => use(0),\n  a: ({ base }, use) => use(base + 1),\n  b: ({ base }, use) => use(base + 1),\n})","handlingStrategy":"validation","validationCode":"// Topologically validate fixture dependencies before extend.\nfunction hasCycle(defs: Record<string, string[]>): boolean {\n  const visited = new Map<string, 'visiting' | 'done'>()\n  function dfs(n: string): boolean {\n    const s = visited.get(n)\n    if (s === 'visiting') return true\n    if (s === 'done') return false\n    visited.set(n, 'visiting')\n    for (const d of defs[n] ?? []) if (dfs(d)) return true\n    visited.set(n, 'done')\n    return false\n  }\n  return Object.keys(defs).some(dfs)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep fixture dependencies acyclic: leaf fixtures depend on nothing.","When overriding a fixture by name, ensure the override still flows dependencies one direction.","Draw the dependency graph when splitting fixtures."],"tags":["fixtures","circular-dependency","dependency-graph"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}