{"record":{"id":"4c361d9bbb115a0c","repo":"tailwindlabs/tailwindcss","slug":"circular-dependency-detected-tocss-node-re","errorCode":null,"errorMessage":"Circular dependency detected:\n\n${toCss([node])}\nRelies on:\n\n${toCss([next])}","messagePattern":"Circular dependency detected:\n\n(.+?)\nRelies on:\n\n(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/tailwindcss/src/apply.ts","lineNumber":131,"sourceCode":"                case 'functional':\n                  if (next.params.replace(/-\\*$/, '') === candidateAstNode.root) {\n                    throw new Error(\n                      `You cannot \\`@apply\\` the \\`${candidate}\\` utility here because it creates a circular dependency.`,\n                    )\n                  }\n                  break\n\n                default:\n                  candidateAstNode satisfies never\n              }\n            }\n          }\n        })\n      }\n\n      // Generic fallback error in case we cannot properly detect the origin of\n      // the circular dependency.\n      throw new Error(\n        `Circular dependency detected:\\n\\n${toCss([node])}\\nRelies on:\\n\\n${toCss([next])}`,\n      )\n    }\n\n    wip.add(node)\n\n    for (let dependencyId of dependencies.get(node)) {\n      for (let dependency of definitions.get(dependencyId)) {\n        path.push(node)\n        visit(dependency, path)\n        path.pop()\n      }\n    }\n\n    seen.add(node)\n    wip.delete(node)\n\n    sorted.push(node)","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/tailwindlabs/tailwindcss/blob/16e94cbf7f965c5ad697e90e940b5e178efad67c/packages/tailwindcss/src/apply.ts#L113-L149","documentation":"After collecting all `@utility`/`@apply` dependencies, `substituteAtApply` performs a topological visit. If it detects a cycle it cannot attribute to a specific self-reference (the targeted check in error 17), it throws a generic fallback that renders the two CSS nodes involved (`node` and `next`) via `toCss`. This is the catch-all for multi-hop cycles like A→B→A.","triggerScenarios":"Two or more utilities that mutually `@apply` each other: `@utility a { @apply b; }` plus `@utility b { @apply a; }`. The DFS `visit` detects a node already in the current `path`/`wip` set and throws at apply.ts:131, showing the CSS of both endpoints.","commonSituations":"Refactoring utilities into a dependency chain that accidentally loops back, or composing multiple `@utility` definitions that each pull in the other.","solutions":["Break the cycle: remove or redirect one of the `@apply` references so the graph becomes acyclic.","Extract the shared styles into a third utility that both reference in one direction only.","Re-run the build; the error names the two CSS blocks to pinpoint the edge to cut."],"exampleFix":"/* before */\n@utility a { @apply b; color: red; }\n@utility b { @apply a; color: blue; }\n\n/* after */\n@utility base { color: red; }\n@utility a { @apply base; }\n@utility b { @apply a; color: blue; }","handlingStrategy":"validation","validationCode":"// Build a utility dependency graph and detect cycles before build\ntype DepGraph = Map<string, Set<string>>\nfunction detectApplyCycle(graph: DepGraph): string[] | null {\n  const WHITE=0, GRAY=1, BLACK=2\n  const color = new Map<string, number>()\n  let cycle: string[] | null = null\n  for (const [n] of graph) {\n    color.set(n, WHITE)\n  }\n  const visit = (n: string, stack: string[]): boolean => {\n    color.set(n, GRAY); stack.push(n)\n    for (const dep of graph.get(n) ?? []) {\n      if (color.get(dep) === GRAY) { cycle = [...stack.slice(stack.indexOf(dep)), dep]; return true }\n      if (color.get(dep) === WHITE && visit(dep, stack)) return true\n    }\n    stack.pop(); color.set(n, BLACK); return false\n  }\n  for (const [n] of graph) if (color.get(n) === WHITE && visit(n, [])) break\n  return cycle\n}","typeGuard":null,"tryCatchPattern":"try {\n  substituteAtApply(ast, designSystem)\n} catch (e) {\n  if (/Circular dependency detected/.test((e as Error).message)) {\n    // inspect the two CSS blocks in the message, cut one @apply edge\n  }\n  throw e\n}","preventionTips":["Keep the @utility/@apply graph acyclic; extract shared base utilities.","Before building, lint utility dependencies for cycles.","Read the two CSS nodes in the error message to find the edge to cut."],"tags":["apply","circular-dependency","utility"],"backgroundTag":null,"analyzedSha":"16e94cbf7f965c5ad697e90e940b5e178efad67c","analyzedAt":"2026-08-12T06:02:42.469Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}