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

gnodet pushed a commit to branch ci-issue-11856
in repository https://gitbox.apache.org/repos/asf/maven.git

commit d8b4c1f220b2c160b36e987aba9ffc16721b6cff
Author: Guillaume Nodet <[email protected]>
AuthorDate: Fri Apr 3 22:28:02 2026 +0200

    Fix #11856: Improve error message for prefix-based remote repository 
filtering errors
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../maven/exception/DefaultExceptionHandler.java   | 38 +++++++++++++++++++++-
 .../exception/DefaultExceptionHandlerTest.java     | 35 ++++++++++++++++++++
 2 files changed, 72 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..297a8b4fd8 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,43 @@ 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 += System.lineSeparator()
+                    + System.lineSeparator()
+                    + "This error indicates that the remote repository's 
prefix file does not list"
+                    + " this artifact's group. This commonly happens with 
repository managers"
+                    + " using virtual/group repositories that do not properly 
aggregate prefix files."
+                    + System.lineSeparator()
+                    + "To disable prefix-based filtering, add"
+                    + " -Daether.remoteRepositoryFilter.prefixes=false"
+                    + " to your command line or to .mvn/maven.config."
+                    + System.lineSeparator()
+                    + "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..ff72133451 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,35 @@ 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 workaround property");
+        assertTrue(summary.getMessage().contains("prefix file"), "Message 
should explain the prefix file 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");

Reply via email to