{"record":{"id":"169630007b5ed659","repo":"nodejs/node","slug":"cycleerror-cycle-involving-s","errorCode":null,"errorMessage":"CycleError: cycle involving: %s","messagePattern":"CycleError: cycle involving: (.+?)","errorType":"exception","errorClass":"CycleError","httpStatus":null,"severity":"error","filePath":"tools/gyp/pylib/gyp/common.py","lineNumber":688,"sourceCode":"      cheaper than repeatedly calling get_edges.\n    Raises:\n      CycleError in the event of a cycle.\n    Example:\n      graph = {'a': '$(b) $(c)', 'b': 'hi', 'c': '$(b)'}\n      def GetEdges(node):\n        return re.findall(r'\\$\\(([^))]\\)', graph[node])\n      print TopologicallySorted(graph.keys(), GetEdges)\n      ==>\n      ['a', 'c', b']\n    \"\"\"\n    get_edges = memoize(get_edges)\n    visited = set()\n    visiting = set()\n    ordered_nodes = []\n\n    def Visit(node):\n        if node in visiting:\n            raise CycleError(visiting)\n        if node in visited:\n            return\n        visited.add(node)\n        visiting.add(node)\n        for neighbor in get_edges(node):\n            Visit(neighbor)\n        visiting.remove(node)\n        ordered_nodes.insert(0, node)\n\n    for node in sorted(graph):\n        Visit(node)\n    return ordered_nodes\n\n\ndef CrossCompileRequested():\n    # TODO: figure out how to not build extra host objects in the\n    # non-cross-compile case when this is enabled, and enable unconditionally.\n    return (","sourceCodeStart":670,"sourceCodeEnd":706,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/gyp/pylib/gyp/common.py#L670-L706","documentation":"TopologicallySorted (gyp/common.py) raises CycleError(visiting) when its depth-first Visit() re-enters a node currently on the recursion stack (the 'visiting' set). The argument is the set of nodes on the current path, so the message lists the nodes forming the cycle. Gyp uses this to order targets/variables; a cycle means a node (directly or transitively) depends on itself, so no valid build order exists.","triggerScenarios":"A gyp dependency graph where target A depends on B and B on A; variable macros that reference each other ($($(A)) style) producing a self-referential edge function; a hand-written get_edges callback that returns an edge back to the start node.","commonSituations":"Refactoring targets and accidentally creating mutual dependencies; merging two .gypi includes that introduce a loop; bad macro expansion producing recursive variable references.","solutions":["Inspect the node set in the CycleError to identify the cycle, then break one edge in the offending .gyp/.gypi.","For variable cycles, audit $(...) macro references and remove the self/loop reference.","Use gyp --debug general or dump the graph to visualize dependencies before sorting.","Ensure get_edges terminates and returns only true forward edges."],"exampleFix":"# before: A -> B -> A\n# target_a.gyp: 'dependencies': ['target_b.gyp:*']\n# target_b.gyp: 'dependencies': ['target_a.gyp:*']\n\n# after: break the back edge\n# target_b.gyp: (remove dependency on target_a)","handlingStrategy":"try-catch","validationCode":"def is_acyclic(graph, get_edges):\n    visiting, visited = set(), set()\n    def visit(n):\n        if n in visiting: return False\n        if n in visited: return True\n        visiting.add(n)\n        for m in get_edges(n):\n            if not visit(m): return False\n        visiting.remove(n); visited.add(n)\n        return True\n    return all(visit(n) for n in graph)","typeGuard":"def graph_is_acyclic(graph, get_edges) -> bool:\n    return is_acyclic(graph, get_edges)","tryCatchPattern":"from gyp.common import TopologicallySorted, CycleError\ntry:\n    order = TopologicallySorted(graph, get_edges)\nexcept CycleError as e:\n    # e.args[0] is the set of nodes on the cycle path\n    print('dependency cycle among:', e.args[0])\n    # break the cycle in the .gyp and retry","preventionTips":["Lint gyp dependency graphs for mutual references before building.","Audit $(...) macro references for self/loop references when sorting variables.","Keep target dependencies strictly layered (no back-edges)."],"tags":["gyp","dependency-graph","cycle","topological-sort","validation"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}