You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/06/01 19:54:41 UTC

(maven) 04/05: Improve error message for prefix-based remote repository filtering (PR #11888)

This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch maven-4.0.x-test-fixes
in repository https://gitbox.apache.org/repos/asf/maven.git

commit ded2a5a7c86cfdec3660fb82a1a19bca9d5338d1
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Mon Jun 1 21:53:52 2026 +0200

    Improve error message for prefix-based remote repository filtering (PR #11888)
    
    Cherry-pick from ci-issue-11856 branch.
    
    Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
 .../maven/exception/DefaultExceptionHandler.java   | 34 +++++++++++++++++-
 .../exception/DefaultExceptionHandlerTest.java     | 40 ++++++++++++++++++++++
 2 files changed, 73 insertions(+), 1 deletion(-)

diff --git a/impl/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java b/impl/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java
index 98db980812..fdf2441bec 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java
@@ -40,6 +40,7 @@
 import org.apache.maven.plugin.PluginExecutionException;
 import org.apache.maven.project.ProjectBuildingException;
 import org.apache.maven.project.ProjectBuildingResult;
+import org.eclipse.aether.transfer.ArtifactFilteredOutException;
 
 /*
 
@@ -177,6 +178,9 @@ private String getReference(Set<Throwable> dejaVu, Throwable exception) {
                         reference = ConnectException.class.getSimpleName();
                     }
                 }
+                if (findCause(exception, ArtifactFilteredOutException.class) != null) {
+                    reference = "https://maven.apache.org/resolver/remote-repository-filtering.html";
+                }
             } else if (exception instanceof LinkageError) {
                 reference = LinkageError.class.getSimpleName();
             } else if (exception instanceof PluginExecutionException) {
@@ -207,7 +211,9 @@ private String getReference(Set<Throwable> dejaVu, Throwable exception) {
             }
         }
 
-        if ((reference != null && !reference.isEmpty()) && !reference.startsWith("http:")) {
+        if ((reference != null && !reference.isEmpty())
+                && !reference.startsWith("http:")
+                && !reference.startsWith("https:")) {
             reference = "http://cwiki.apache.org/confluence/display/MAVEN/" + reference;
         }
 
@@ -229,6 +235,8 @@ private boolean isNoteworthyException(Throwable exception) {
     private String getMessage(String message, Throwable exception) {
         String fullMessage = (message != null) ? message : "";
 
+        boolean hasArtifactFilteredOut = false;
+
         // To break out of possible endless loop when getCause returns "this", or dejaVu for n-level recursion (n>1)
         Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<>());
         for (Throwable t = exception; t != null && t != t.getCause(); t = t.getCause()) {
@@ -260,15 +268,39 @@ private String getMessage(String message, Throwable exception) {
                 fullMessage = join(fullMessage, exceptionMessage);
             }
 
+            if (t instanceof ArtifactFilteredOutException) {
+                hasArtifactFilteredOut = true;
+            }
+
             if (!dejaVu.add(t)) {
                 fullMessage = join(fullMessage, "[CIRCULAR REFERENCE]");
                 break;
             }
         }
 
+        if (hasArtifactFilteredOut) {
+            fullMessage += """
+
+                This error indicates that a remote repository filter has rejected this artifact. This commonly happens with repository managers using virtual/group repositories that do not properly aggregate prefix files.
+                To disable remote repository filtering, add one or both of these to your command line or to .mvn/maven.config:
+                  -Daether.remoteRepositoryFilter.prefixes=false
+                  -Daether.remoteRepositoryFilter.groupId=false
+                See https://maven.apache.org/resolver/remote-repository-filtering.html""";
+        }
+
         return fullMessage.trim();
     }
 
+    private static <T extends Throwable> T findCause(Throwable exception, Class<T> type) {
+        Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<>());
+        for (Throwable t = exception; t != null && dejaVu.add(t); t = t.getCause()) {
+            if (type.isInstance(t)) {
+                return type.cast(t);
+            }
+        }
+        return null;
+    }
+
     private String join(String message1, String message2) {
         String message = "";
 
diff --git a/impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java b/impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java
index c9a84014d2..88359e2e8f 100644
--- a/impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java
+++ b/impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java
@@ -29,9 +29,15 @@
 import org.apache.maven.plugin.PluginExecutionException;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.transfer.ArtifactFilteredOutException;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  */
@@ -124,6 +130,40 @@ public synchronized Throwable getCause() {
         assertEquals(expectedReference, summary.getReference());
     }
 
+    @Test
+    void testArtifactFilteredOutException() {
+        RemoteRepository repo =
+                new RemoteRepository.Builder("my-repo", "default", "https://repo.example.com/maven").build();
+        ArtifactFilteredOutException filterEx = new ArtifactFilteredOutException(
+                new DefaultArtifact("com.example:my-lib:jar:1.0"),
+                repo,
+                "Prefix com/example/my-lib/1.0/my-lib-1.0.jar NOT allowed from my-repo"
+                        + " (https://repo.example.com/maven, default, releases)");
+        ArtifactResult artifactResult = new ArtifactResult(new org.eclipse.aether.resolution.ArtifactRequest(
+                new DefaultArtifact("com.example:my-lib:jar:1.0"), java.util.List.of(repo), null));
+        artifactResult.addException(filterEx);
+        ArtifactResolutionException resolutionEx =
+                new ArtifactResolutionException(java.util.List.of(artifactResult), "Could not resolve artifact");
+        MojoExecutionException mojoEx = new MojoExecutionException("Resolution failed", resolutionEx);
+
+        DefaultExceptionHandler handler = new DefaultExceptionHandler();
+        ExceptionSummary summary = handler.handleException(mojoEx);
+
+        assertTrue(
+                summary.getMessage().contains("-Daether.remoteRepositoryFilter.prefixes=false"),
+                "Message should contain the prefixes workaround property");
+        assertTrue(
+                summary.getMessage().contains("-Daether.remoteRepositoryFilter.groupId=false"),
+                "Message should contain the groupId workaround property");
+        assertTrue(
+                summary.getMessage().contains("remote repository filter"),
+                "Message should explain the filtering cause");
+        assertEquals(
+                "https://maven.apache.org/resolver/remote-repository-filtering.html",
+                summary.getReference(),
+                "Reference should point to the RRF documentation");
+    }
+
     @Test
     void testHandleExceptionSelfReferencing() {
         RuntimeException boom3 = new RuntimeException("BOOM3");