Skip to content

Commit cee05ae

Browse files
gnodetclaude
andauthored
Fix #11856: Improve error message for prefix-based remote repository filtering errors (#11888) (#12234)
* Fix #11856: Improve error message for prefix-based remote repository filtering errors * Fix #11856: Mention both prefixes and groupId filters in error message The ArtifactFilteredOutException can be triggered by either the prefixes or the groupId remote repository filter. Update the error message to mention both properties so users know which to disable. * Refactor: Convert error message string concatenation to text block Replace multi-line string concatenation with System.lineSeparator() calls with a cleaner text block in DefaultExceptionHandler. The error message for ArtifactFilteredOutException now uses Java text blocks (triple-quoted strings) instead of the + operator, improving readability and maintainability. --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Claude Code <claude@anthropic.com>
1 parent c951af7 commit cee05ae

2 files changed

Lines changed: 73 additions & 1 deletion

File tree

impl/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.apache.maven.plugin.PluginExecutionException;
4141
import org.apache.maven.project.ProjectBuildingException;
4242
import org.apache.maven.project.ProjectBuildingResult;
43+
import org.eclipse.aether.transfer.ArtifactFilteredOutException;
4344

4445
/*
4546
@@ -177,6 +178,9 @@ private String getReference(Set<Throwable> dejaVu, Throwable exception) {
177178
reference = ConnectException.class.getSimpleName();
178179
}
179180
}
181+
if (findCause(exception, ArtifactFilteredOutException.class) != null) {
182+
reference = "https://maven.apache.org/resolver/remote-repository-filtering.html";
183+
}
180184
} else if (exception instanceof LinkageError) {
181185
reference = LinkageError.class.getSimpleName();
182186
} else if (exception instanceof PluginExecutionException) {
@@ -207,7 +211,9 @@ private String getReference(Set<Throwable> dejaVu, Throwable exception) {
207211
}
208212
}
209213

210-
if ((reference != null && !reference.isEmpty()) && !reference.startsWith("http:")) {
214+
if ((reference != null && !reference.isEmpty())
215+
&& !reference.startsWith("http:")
216+
&& !reference.startsWith("https:")) {
211217
reference = "http://cwiki.apache.org/confluence/display/MAVEN/" + reference;
212218
}
213219

@@ -229,6 +235,8 @@ private boolean isNoteworthyException(Throwable exception) {
229235
private String getMessage(String message, Throwable exception) {
230236
String fullMessage = (message != null) ? message : "";
231237

238+
boolean hasArtifactFilteredOut = false;
239+
232240
// To break out of possible endless loop when getCause returns "this", or dejaVu for n-level recursion (n>1)
233241
Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<>());
234242
for (Throwable t = exception; t != null && t != t.getCause(); t = t.getCause()) {
@@ -260,15 +268,39 @@ private String getMessage(String message, Throwable exception) {
260268
fullMessage = join(fullMessage, exceptionMessage);
261269
}
262270

271+
if (t instanceof ArtifactFilteredOutException) {
272+
hasArtifactFilteredOut = true;
273+
}
274+
263275
if (!dejaVu.add(t)) {
264276
fullMessage = join(fullMessage, "[CIRCULAR REFERENCE]");
265277
break;
266278
}
267279
}
268280

281+
if (hasArtifactFilteredOut) {
282+
fullMessage += """
283+
284+
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.
285+
To disable remote repository filtering, add one or both of these to your command line or to .mvn/maven.config:
286+
-Daether.remoteRepositoryFilter.prefixes=false
287+
-Daether.remoteRepositoryFilter.groupId=false
288+
See https://maven.apache.org/resolver/remote-repository-filtering.html""";
289+
}
290+
269291
return fullMessage.trim();
270292
}
271293

294+
private static <T extends Throwable> T findCause(Throwable exception, Class<T> type) {
295+
Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<>());
296+
for (Throwable t = exception; t != null && dejaVu.add(t); t = t.getCause()) {
297+
if (type.isInstance(t)) {
298+
return type.cast(t);
299+
}
300+
}
301+
return null;
302+
}
303+
272304
private String join(String message1, String message2) {
273305
String message = "";
274306

impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,15 @@
2929
import org.apache.maven.plugin.PluginExecutionException;
3030
import org.apache.maven.plugin.descriptor.MojoDescriptor;
3131
import org.apache.maven.plugin.descriptor.PluginDescriptor;
32+
import org.eclipse.aether.artifact.DefaultArtifact;
33+
import org.eclipse.aether.repository.RemoteRepository;
34+
import org.eclipse.aether.resolution.ArtifactResolutionException;
35+
import org.eclipse.aether.resolution.ArtifactResult;
36+
import org.eclipse.aether.transfer.ArtifactFilteredOutException;
3237
import org.junit.jupiter.api.Test;
3338

3439
import static org.junit.jupiter.api.Assertions.assertEquals;
40+
import static org.junit.jupiter.api.Assertions.assertTrue;
3541

3642
/**
3743
*/
@@ -124,6 +130,40 @@ public synchronized Throwable getCause() {
124130
assertEquals(expectedReference, summary.getReference());
125131
}
126132

133+
@Test
134+
void testArtifactFilteredOutException() {
135+
RemoteRepository repo =
136+
new RemoteRepository.Builder("my-repo", "default", "https://repo.example.com/maven").build();
137+
ArtifactFilteredOutException filterEx = new ArtifactFilteredOutException(
138+
new DefaultArtifact("com.example:my-lib:jar:1.0"),
139+
repo,
140+
"Prefix com/example/my-lib/1.0/my-lib-1.0.jar NOT allowed from my-repo"
141+
+ " (https://repo.example.com/maven, default, releases)");
142+
ArtifactResult artifactResult = new ArtifactResult(new org.eclipse.aether.resolution.ArtifactRequest(
143+
new DefaultArtifact("com.example:my-lib:jar:1.0"), java.util.List.of(repo), null));
144+
artifactResult.addException(filterEx);
145+
ArtifactResolutionException resolutionEx =
146+
new ArtifactResolutionException(java.util.List.of(artifactResult), "Could not resolve artifact");
147+
MojoExecutionException mojoEx = new MojoExecutionException("Resolution failed", resolutionEx);
148+
149+
DefaultExceptionHandler handler = new DefaultExceptionHandler();
150+
ExceptionSummary summary = handler.handleException(mojoEx);
151+
152+
assertTrue(
153+
summary.getMessage().contains("-Daether.remoteRepositoryFilter.prefixes=false"),
154+
"Message should contain the prefixes workaround property");
155+
assertTrue(
156+
summary.getMessage().contains("-Daether.remoteRepositoryFilter.groupId=false"),
157+
"Message should contain the groupId workaround property");
158+
assertTrue(
159+
summary.getMessage().contains("remote repository filter"),
160+
"Message should explain the filtering cause");
161+
assertEquals(
162+
"https://maven.apache.org/resolver/remote-repository-filtering.html",
163+
summary.getReference(),
164+
"Reference should point to the RRF documentation");
165+
}
166+
127167
@Test
128168
void testHandleExceptionSelfReferencing() {
129169
RuntimeException boom3 = new RuntimeException("BOOM3");

0 commit comments

Comments
 (0)