{"record":{"id":"86801d8c89b32ade","repo":"tailwindlabs/tailwindcss","slug":"circular-dependency-detected-in-custom-variants","errorCode":null,"errorMessage":"Circular dependency detected in custom variants:\n\n${output}","messagePattern":"Circular dependency detected in custom variants:\n\n(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/tailwindcss/src/index.ts","lineNumber":644,"sourceCode":"\n  for (let name of customVariants.keys()) {\n    // Pre-register the variant to ensure its position in the variant list is\n    // based on the order we see them in the CSS.\n    designSystem.variants.static(name, () => {})\n  }\n\n  // Register custom variants in order\n  for (let variant of topologicalSort(customVariantDependencies, {\n    onCircularDependency(path, start) {\n      let output = toCss(\n        path.map((name, idx) => {\n          return atRule('@custom-variant', name, [atRule('@variant', path[idx + 1] ?? start, [])])\n        }),\n      )\n        .replaceAll(';', ' { … }')\n        .replace(`@custom-variant ${start} {`, `@custom-variant ${start} { /* ← */`)\n\n      throw new Error(`Circular dependency detected in custom variants:\\n\\n${output}`)\n    },\n  })) {\n    customVariants.get(variant)?.(designSystem)\n  }\n\n  for (let customUtility of customUtilities) {\n    customUtility(designSystem)\n  }\n\n  // Output final set of theme variables at the position of the first\n  // `@theme` rule.\n  if (firstThemeRule) {\n    let nodes = []\n\n    for (let [key, value] of designSystem.theme.entries()) {\n      if (value.options & ThemeOptions.REFERENCE) continue\n      let node = decl(escape(key), value.value)\n      node.src = value.src","sourceCodeStart":626,"sourceCodeEnd":662,"githubUrl":"https://github.com/tailwindlabs/tailwindcss/blob/16e94cbf7f965c5ad697e90e940b5e178efad67c/packages/tailwindcss/src/index.ts#L626-L662","documentation":"Thrown by `topologicalSort` when `@custom-variant` rules reference each other via `@variant` in a way that forms a cycle (e.g. variant A references B, B references C, C references A). The error message renders the cycle as CSS with a `/* ← */` marker on the starting node so the loop is visible. Tailwind refuses to register variants in an undefined order.","triggerScenarios":"Defining `@custom-variant a { @variant b; }`, `@custom-variant b { @variant a; }` — or any longer cycle across `@custom-variant` body-form rules.","commonSituations":"Refactoring variants and accidentally introducing mutual references; renaming a variant but forgetting to update a reference that closes a loop.","solutions":["Break the cycle: identify the back-edge from the error's rendered output and remove or retarget one `@variant` reference.","If two variants are truly equivalent, alias them in one direction only (`@custom-variant a { @variant b; }` and stop).","Flatten mutually-dependent variants into a single `@custom-variant` that lists all selectors directly."],"exampleFix":"/* before */\n@custom-variant a { @variant b; }\n@custom-variant b { @variant a; }\n\n/* after */\n@custom-variant a (&:hover);\n@custom-variant b { @variant a; }","handlingStrategy":"validation","validationCode":"// Detect cycles in @custom-variant dependencies before compiling.\nimport postcss from 'postcss'\n\nfunction detectCustomVariantCycles(css: string): string[][] {\n  const graph = new Map<string, Set<string>>()\n  postcss.parse(css).walkAtRules('@custom-variant', (rule) => {\n    const name = rule.params.split(' ')[0]\n    const deps = new Set<string>()\n    rule.walkAtRules('@variant', (v) => deps.add(v.params.split(' ')[0]))\n    graph.set(name, deps)\n  })\n\n  const cycles: string[][] = []\n  const visited = new Set<string>()\n  const stack: string[] = []\n  function dfs(node: string) {\n    if (stack.includes(node)) {\n      cycles.push(stack.slice(stack.indexOf(node)).concat(node))\n      return\n    }\n    if (visited.has(node)) return\n    visited.add(node)\n    stack.push(node)\n    for (const dep of graph.get(node) ?? []) dfs(dep)\n    stack.pop()\n  }\n  for (const node of graph.keys()) dfs(node)\n  return cycles\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep `@custom-variant` references acyclic — alias in one direction only.","When renaming a variant, grep for `@variant <old-name>` and update every reference to avoid closing a loop.","Run a cycle-detection pass in CI for `@custom-variant` rules."],"tags":["at-custom-variant","circular-dependency","topological-sort"],"backgroundTag":null,"analyzedSha":"16e94cbf7f965c5ad697e90e940b5e178efad67c","analyzedAt":"2026-08-12T06:02:42.469Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}