{"record":{"id":"0e4f8a1858091b27","repo":"apache/maven","slug":"artifact-relocations-form-a-cycle-visited","errorCode":null,"errorMessage":"Artifact relocations form a cycle: {visited}","messagePattern":"Artifact relocations form a cycle: (.+?)","errorType":"exception","errorClass":"RepositoryException","httpStatus":null,"severity":"error","filePath":"compat/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReader.java","lineNumber":167,"sourceCode":"                versionRequest.setTrace(trace);\n                VersionResult versionResult = versionResolver.resolveVersion(session, versionRequest);\n\n                a = a.setVersion(versionResult.getVersion());\n\n                versionRequest =\n                        new VersionRequest(pomArtifact, request.getRepositories(), request.getRequestContext());\n                versionRequest.setTrace(trace);\n                versionResult = versionResolver.resolveVersion(session, versionRequest);\n\n                pomArtifact = pomArtifact.setVersion(versionResult.getVersion());\n            } catch (VersionResolutionException e) {\n                result.addException(e);\n                throw new ArtifactDescriptorException(result);\n            }\n\n            if (!visited.add(a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getBaseVersion())) {\n                RepositoryException exception =\n                        new RepositoryException(\"Artifact relocations form a cycle: \" + visited);\n                invalidDescriptor(session, trace, a, exception);\n                if ((getPolicy(session, a, request) & ArtifactDescriptorPolicy.IGNORE_INVALID) != 0) {\n                    return null;\n                }\n                result.addException(exception);\n                throw new ArtifactDescriptorException(result);\n            }\n\n            ArtifactResult resolveResult;\n            try {\n                ArtifactRequest resolveRequest =\n                        new ArtifactRequest(pomArtifact, request.getRepositories(), request.getRequestContext());\n                resolveRequest.setTrace(trace);\n                resolveResult = artifactResolver.resolveArtifact(session, resolveRequest);\n                pomArtifact = resolveResult.getArtifact();\n                result.setRepository(resolveResult.getRepository());\n            } catch (ArtifactResolutionException e) {\n                if (e.getCause() instanceof ArtifactNotFoundException artifactNotFoundException) {","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/apache/maven/blob/e4093d4e120eac99d6bdce5ba67cace2f3085c97/compat/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReader.java#L149-L185","documentation":"DefaultArtifactDescriptorReader follows <relocation> entries in POMs, guarded by a visited set of groupId:artifactId:baseVersion. Revisiting an entry, i.e. artifact A relocates to B which relocates back to A, aborts with RepositoryException 'Artifact relocations form a cycle' listing the chain; the build then fails with ArtifactDescriptorException unless the artifact-descriptor policy ignores invalid descriptors (in which case the descriptor resolves to null).","triggerScenarios":"Resolving an artifact whose relocation chain returns to an already-visited coordinate:baseVersion, mutual relocations between two artifacts, or a POM relocating to its own coordinates at a version that normalizes to the same base version.","commonSituations":"Coordinate renames where both sides publish relocations pointing at each other; repository-manager rewrite rules that inject looping relocations; publishing automation that copies relocation blocks between related POMs.","solutions":["Inspect the chain printed in the message and fix the looping <relocation> in the offending POM(s): each relocation must move to a strictly new coordinate","If you do not own those POMs, pin the dependency to a version whose relocation is well-formed, or exclude/replace the artifact with its relocation target","Repository managers: audit rewrite rules for mutual relocations","As a stopgap, a lenient artifact-descriptor policy (IGNORE_INVALID) makes the reader skip the broken descriptor instead of failing the build"],"exampleFix":"<!-- before: old-lib POM relocates to new-lib, and new-lib POM relocates\n     back to old-lib -> cycle: old-lib -> new-lib -> old-lib -->\n<project>\n  <groupId>com.example</groupId>\n  <artifactId>old-lib</artifactId><version>1.9</version>\n  <distributionManagement>\n    <relocation>\n      <groupId>com.example</groupId><artifactId>new-lib</artifactId>\n    </relocation>\n  </distributionManagement>\n</project>\n\n<!-- after: relocate only forward, never point back -->\n<project>\n  <groupId>com.example</groupId>\n  <artifactId>old-lib</artifactId><version>1.9</version>\n  <distributionManagement>\n    <relocation>\n      <groupId>com.example</groupId><artifactId>new-lib</artifactId>\n    </relocation>\n  </distributionManagement>\n</project>\n<!-- and new-lib's POM contains NO relocation back to old-lib -->","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    descriptor = descriptorReader.readArtifactDescriptor(session, request);\n} catch (ArtifactDescriptorException e) {\n    boolean relocationCycle = e.getExceptions().stream()\n            .anyMatch(t -> t.getMessage() != null\n                    && t.getMessage().contains(\"relocations form a cycle\"));\n    if (relocationCycle) {\n        // pin the relocation target explicitly instead of following the loop\n        dependencies = replaceWithRelocationTarget(dependencies);\n    } else {\n        throw e;\n    }\n}","preventionTips":["When renaming coordinates, publish only forward relocations and verify chains manually","Audit repository-manager rewrite rules for mutual relocations","After any relocation change, resolve the artifact once from a clean local repo to catch cycles"],"tags":["maven","resolver","relocation","cycle","dependencies"],"backgroundTag":"circular-dependency","analyzedSha":"e4093d4e120eac99d6bdce5ba67cace2f3085c97","analyzedAt":"2026-08-21T22:58:24.034Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}