apache/maven · error · RepositoryException
Artifact relocations form a cycle: {visited}
Error message
Artifact relocations form a cycle: {visited} What it means
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).
Source
Thrown at compat/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReader.java:167
versionRequest.setTrace(trace);
VersionResult versionResult = versionResolver.resolveVersion(session, versionRequest);
a = a.setVersion(versionResult.getVersion());
versionRequest =
new VersionRequest(pomArtifact, request.getRepositories(), request.getRequestContext());
versionRequest.setTrace(trace);
versionResult = versionResolver.resolveVersion(session, versionRequest);
pomArtifact = pomArtifact.setVersion(versionResult.getVersion());
} catch (VersionResolutionException e) {
result.addException(e);
throw new ArtifactDescriptorException(result);
}
if (!visited.add(a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getBaseVersion())) {
RepositoryException exception =
new RepositoryException("Artifact relocations form a cycle: " + visited);
invalidDescriptor(session, trace, a, exception);
if ((getPolicy(session, a, request) & ArtifactDescriptorPolicy.IGNORE_INVALID) != 0) {
return null;
}
result.addException(exception);
throw new ArtifactDescriptorException(result);
}
ArtifactResult resolveResult;
try {
ArtifactRequest resolveRequest =
new ArtifactRequest(pomArtifact, request.getRepositories(), request.getRequestContext());
resolveRequest.setTrace(trace);
resolveResult = artifactResolver.resolveArtifact(session, resolveRequest);
pomArtifact = resolveResult.getArtifact();
result.setRepository(resolveResult.getRepository());
} catch (ArtifactResolutionException e) {
if (e.getCause() instanceof ArtifactNotFoundException artifactNotFoundException) {View on GitHub (pinned to e4093d4e12)
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
Example fix
<!-- before: old-lib POM relocates to new-lib, and new-lib POM relocates
back to old-lib -> cycle: old-lib -> new-lib -> old-lib -->
<project>
<groupId>com.example</groupId>
<artifactId>old-lib</artifactId><version>1.9</version>
<distributionManagement>
<relocation>
<groupId>com.example</groupId><artifactId>new-lib</artifactId>
</relocation>
</distributionManagement>
</project>
<!-- after: relocate only forward, never point back -->
<project>
<groupId>com.example</groupId>
<artifactId>old-lib</artifactId><version>1.9</version>
<distributionManagement>
<relocation>
<groupId>com.example</groupId><artifactId>new-lib</artifactId>
</relocation>
</distributionManagement>
</project>
<!-- and new-lib's POM contains NO relocation back to old-lib --> Defensive patterns
Strategy: try-catch
Try / catch
try {
descriptor = descriptorReader.readArtifactDescriptor(session, request);
} catch (ArtifactDescriptorException e) {
boolean relocationCycle = e.getExceptions().stream()
.anyMatch(t -> t.getMessage() != null
&& t.getMessage().contains("relocations form a cycle"));
if (relocationCycle) {
// pin the relocation target explicitly instead of following the loop
dependencies = replaceWithRelocationTarget(dependencies);
} else {
throw e;
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- groupId can neither be null, empty nor blank
- The artifact {} has been relocated to {}
- Repository list contains null entries. All repository entrie
- Error updating group repository metadata
- Unable to lookup org.eclipse.aether.RepositorySystem
AI-assisted analysis of apache/maven@e4093d4e12 (2026-08-21).
Data as JSON: /api/errors/0e4f8a1858091b27.
Report an issue: GitHub.