{"record":{"id":"49e9149f56ce9549","repo":"apache/maven","slug":"edge-between-from-and-to-introduces-to-c","errorCode":null,"errorMessage":"Edge between '${from}' and '${to}' 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-impl/src/main/java/org/apache/maven/impl/model/Graph.java","lineNumber":40,"sourceCode":"import java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nclass Graph {\n\n    final Map<String, Set<String>> graph = new LinkedHashMap<>();\n\n    synchronized void addEdge(String from, String to) throws CycleDetectedException {\n        if (graph.computeIfAbsent(from, l -> new HashSet<>()).add(to)) {\n            List<String> cycle = visitCycle(graph, Collections.singleton(to), new HashMap<>(), new LinkedList<>());\n            if (cycle != null) {\n                // remove edge which introduced cycle\n                throw new CycleDetectedException(\n                        \"Edge between '\" + from + \"' and '\" + to + \"' introduces to cycle in the graph\", cycle);\n            }\n        }\n    }\n\n    private enum DfsState {\n        VISITING,\n        VISITED\n    }\n\n    private static List<String> visitCycle(\n            Map<String, Set<String>> graph,\n            Collection<String> children,\n            Map<String, DfsState> stateMap,\n            LinkedList<String> cycle) {\n        if (children != null) {\n            for (String v : children) {\n                DfsState state = stateMap.putIfAbsent(v, DfsState.VISITING);","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/apache/maven/blob/e4093d4e120eac99d6bdce5ba67cace2f3085c97/impl/maven-impl/src/main/java/org/apache/maven/impl/model/Graph.java#L22-L58","documentation":"Graph is the small graph DefaultModelBuilder uses to record model relations (parent/child, relativePath edges) during recursive builds. addEdge() inserts the edge, runs a DFS cycle check, and if the new edge closes a loop it removes the edge and throws CycleDetectedException carrying the cycle path. At the model-builder level this surfaces as the FATAL problem 'Cycle detected between models at X and Y'.","triggerScenarios":"Pom A declares pom B as its parent while B (or something in B's parent chain) declares A as its parent; <relativePath> values that make the inheritance graph loop back on itself.","commonSituations":"Hand-edited multi-module projects where parent/child relations were swapped; an aggregator accidentally declared as parent of its own parent; refactoring that moved parent poms but left stale relativePath values pointing at children.","solutions":["Map the parent chain of the two poms named in the message and break the loop: every chain must terminate at a root pom that has no parent","Fix or remove <relativePath> entries so each pom resolves its true parent","Make sure the root/aggregator pom is never also the parent of a pom above it","Run mvn -e to see the full cycle path carried by the exception"],"exampleFix":"<!-- before: child/pom.xml has parent = root, but root/pom.xml also claims child as its parent -->\n<!-- fix: keep the chain one-directional -->\n<parent>\n  <groupId>com.acme</groupId>\n  <artifactId>root</artifactId>\n  <relativePath>../pom.xml</relativePath>\n</parent>\n<!-- root pom must NOT declare <parent> pointing back at child -->","handlingStrategy":"validation","validationCode":"// Pre-validate the inheritance graph: walk each pom's parent chain and detect repeats\nstatic Optional<Path> findCycle(Map<Path, Path> parentOf) { // child -> resolved parent pom\n    for (Path start : parentOf.keySet()) {\n        Set<Path> seen = new HashSet<>();\n        for (Path p = start; p != null; p = parentOf.get(p)) {\n            if (!seen.add(p)) return Optional.of(p);\n        }\n    }\n    return Optional.empty();\n}","typeGuard":null,"tryCatchPattern":"try {\n    resultGraph.addEdge(from, to);\n} catch (Graph.CycleDetectedException e) {\n    // e.getCycle() lists the loop; report it and stop instead of retrying\n}","preventionTips":["Enforce a single root pom with no <parent> and keep all inheritance edges pointing 'up' the tree","Validate parent/relativePath pairs in CI (e.g. mvn validate -q or a custom checker) before deep builds","After moving poms, grep all <relativePath> values for references that now point at children"],"tags":["maven","model-building","cycle","parent-pom","multi-module"],"backgroundTag":"circular-module-inheritance","analyzedSha":"e4093d4e120eac99d6bdce5ba67cace2f3085c97","analyzedAt":"2026-08-21T22:58:24.034Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}