How to Fix a Maven Dependency Loop (Used declared vs Unused declared)

Published Jul 15, 2022

I ran into a dependency loop of sorts in Maven when running mvn clean verify.

Used undeclared dependencies found

I had the following dependency in my pom.xml.

<dependency>
  <groupId>org.apache.hadoop</groupId>
  <artifactId>hadoop-client-api</artifactId>
</dependency>

Running mvn clean verify would yield a Used undeclared dependencies found warning.

[WARNING] Used undeclared dependencies found:
[WARNING]    org.apache.hadoop:hadoop-common:jar:VERSION:compile
[WARNING]     - org.apache.hadoop.fs.Path is referenced in com.some.package
[WARNING] 
[WARNING] Unused declared dependencies found:
[WARNING]    org.apache.hadoop:hadoop-client-api:jar:VERSION:compile

Unused declared dependencies found

Naturally, I followed the instructions: added the used undeclared dependency and removed the unused declared dependency.

<dependency>
  <groupId>org.apache.hadoop</groupId>
  <artifactId>hadoop-common</artifactId>
</dependency>

But another run of mvn clean verify yielded an Unused declared dependencies found warning.

[WARNING] Unused declared dependencies found:
[WARNING]    org.apache.hadoop:hadoop-common:jar:VERSION:compile

Dependency loop explanation

The first thing to note is that both of the artifacts hadoop-client-api and hadoop-common expose the aforementioned class org.apache.hadoop.fs.Path.

This issue arises when the same class is being pulled from two different dependencies. One copy of the class is likely being directly exposed in one dependency and transitively exposed in another.

The dependency that appears first in the pom.xml is the one that ends up loaded on the classpath.

Some dependencies need to be on the classpath in a certain order. Unfortunately, the wrong order will trigger this dependency loop.

First, we’ll want to isolate the artifact that’s also pulling in the specified class.

Printing out the dependency tree with mvn dependency:tree will help identify that artifact (a child dependency is likely the one pulling in the overlapping set of files).

Once that artifact is found, we can simply reorder the dependencies such that the one that directly exposes the class comes first.