{"record":{"id":"d04d5fc5e28f2279","repo":"apache/maven","slug":"edge-between-and-introduces-to-cycle-in","errorCode":null,"errorMessage":"Edge between '{}' and '{}' introduces to cycle in the graph","messagePattern":"Edge between '(.+?)' and '(.+?)' introduces to cycle in the graph","errorType":"exception","errorClass":"CycleDetectedException","httpStatus":null,"severity":"error","filePath":"impl/maven-core/src/main/java/org/apache/maven/internal/impl/Graph.java","lineNumber":57,"sourceCode":"        return vertices.get(id);\n    }\n\n    public Collection<Vertex> getVertices() {\n        return vertices.values();\n    }\n\n    Vertex addVertex(String label) {\n        return vertices.computeIfAbsent(label, Vertex::new);\n    }\n\n    void addEdge(Vertex from, Vertex to) throws CycleDetectedException {\n        from.children.add(to);\n        to.parents.add(from);\n        List<String> cycle = findCycle(to);\n        if (cycle != null) {\n            // remove edge which introduced cycle\n            removeEdge(from, to);\n            throw new CycleDetectedException(\n                    \"Edge between '\" + from.label + \"' and '\" + to.label + \"' introduces to cycle in the graph\", cycle);\n        }\n    }\n\n    void removeEdge(Vertex from, Vertex to) {\n        from.children.remove(to);\n        to.parents.remove(from);\n    }\n\n    List<String> visitAll() {\n        return visitAll(vertices.values(), new HashMap<>(), new ArrayList<>());\n    }\n\n    List<String> findCycle(Vertex vertex) {\n        return visitCycle(Collections.singleton(vertex), new HashMap<>(), new LinkedList<>());\n    }\n\n    private static List<String> visitAll(","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/apache/maven/blob/e4093d4e120eac99d6bdce5ba67cace2f3085c97/impl/maven-core/src/main/java/org/apache/maven/internal/impl/Graph.java#L39-L75","documentation":"CycleDetectedException is thrown by Graph.addEdge() when adding an edge (a dependency relationship) would create a directed cycle in the project graph. The edge is rolled back before throwing, and the exception carries the list of vertex labels forming the cycle. This graph is used by the reactor's project sorter (ProjectSorter), so the vertices are Maven module ids and the cycle means module A depends on module B while B (transitively) depends on A.","triggerScenarios":"Building a multi-module reactor where module interdependencies form a cycle: module A declares a <dependency> on B and B declares one on A; or a longer cycle A->B->C->A created by adding one dependency between modules; also cycles introduced via plugin <extensions> ordering or parent/child module reference mistakes during reactor sorting.","commonSituations":"Adding an inter-module dependency without checking the existing direction; refactoring that moves a class into another module while both modules still reference each other; accidental cyclic dependency introduced by generating code into the wrong module; test-support modules that depend on the module under test.","solutions":["Read the cycle path in the exception message: it names the modules forming the loop, e.g. [A -> B -> A]","Break the cycle by removing or reversing one dependency edge — typically by extracting the shared code into a third module both depend on","If one edge is only needed for tests, move that test to the other module or use test-jar artifacts carefully so the compile-time graph stays acyclic","Re-run 'mvn validate' after each edge change to confirm the reactor sorts again"],"exampleFix":"<!-- before: a/pom.xml and b/pom.xml depend on each other -->\n<dependency><groupId>org.acme</groupId><artifactId>b</artifactId></dependency> <!-- in a -->\n<dependency><groupId>org.acme</groupId><artifactId>a</artifactId></dependency> <!-- in b -->\n\n<!-- after: extract shared code into 'common' -->\n<dependency><groupId>org.acme</groupId><artifactId>common</artifactId></dependency> <!-- in both a and b -->","handlingStrategy":"validation","validationCode":"// before adding an inter-module dependency, check the reverse direction\nOptional<Dependency> reverse = project.getDependencies().stream()\n    .filter(d -> d.getArtifactId().equals(otherModuleArtifactId)).findFirst();\nif (reverse.isPresent()) throw new IllegalStateException(\"Would create a module cycle\");","typeGuard":null,"tryCatchPattern":"try {\n    ProjectSorter sorter = new ProjectSorter(projects);\n} catch (CycleDetectedException e) {\n    List<String> cycle = e.getCycle();\n    // report the module loop and fail the pipeline with a clear message\n}","preventionTips":["Keep inter-module dependencies one-directional: lower layers depend on higher, never back","Extract shared code into a common module instead of adding back-references","Run 'mvn validate' after every dependency change in a multi-module reactor"],"tags":["maven","cycle","reactor","multi-module","dependency-graph"],"backgroundTag":"circular-dependency-detected","analyzedSha":"e4093d4e120eac99d6bdce5ba67cace2f3085c97","analyzedAt":"2026-08-21T22:58:24.034Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}