Introduction

Maven resolves dependency version conflicts using "nearest definition" - the version closest to your pom.xml in the dependency tree wins. When two transitive dependencies pull in different versions of the same library, the older version may be selected, causing NoSuchMethodError at runtime when your code calls a method that exists only in the newer version.

Symptoms

  • java.lang.NoSuchMethodError: com.google.common.collect.ImmutableList.toImmutableList()
  • java.lang.NoSuchMethodError: org.apache.commons.lang3.StringUtils.isBlank(Ljava/lang/CharSequence;)Z
  • Application compiles but fails at runtime
  • Works in one module, fails in another with different dependencies
  • Error message shows the method signature that is missing
bash
Exception in thread "main" java.lang.NoSuchMethodError:
'java.util.stream.Collector com.google.common.collect.ImmutableList.toImmutableList()'
    at com.example.Service.process(Service.java:42)
    at com.example.App.main(App.java:15)
# toImmutableList() was added in Guava 21.0
# But Maven resolved Guava 19.0 from a transitive dependency

Common Causes

  • Two dependencies requiring different versions of the same library
  • Transitive dependency pulling in an older version
  • BOM (Bill of Materials) overriding explicit version declarations
  • Parent POM managing versions that conflict with direct dependencies
  • Library upgrade without checking transitive dependency impact

Step-by-Step Fix

  1. 1.Find the conflicting dependency:
  2. 2.```bash
  3. 3.# Show full dependency tree
  4. 4.mvn dependency:tree

# Find which artifact brings in the conflicting version mvn dependency:tree -Dincludes=com.google.guava:guava

# Output might show: # [INFO] +- com.example:lib-a:jar:1.0 # [INFO] | - com.google.guava:guava:jar:19.0:compile # [INFO] +- com.example:lib-b:jar:2.0 # [INFO] | - com.google.guava:guava:jar:32.0:compile # Maven picks 19.0 (nearest in tree) - but you need 32.0! ```

  1. 1.Exclude the older transitive version:
  2. 2.```xml
  3. 3.<dependency>
  4. 4.<groupId>com.example</groupId>
  5. 5.<artifactId>lib-a</artifactId>
  6. 6.<version>1.0</version>
  7. 7.<exclusions>
  8. 8.<exclusion>
  9. 9.<groupId>com.google.guava</groupId>
  10. 10.<artifactId>guava</artifactId>
  11. 11.</exclusion>
  12. 12.</exclusions>
  13. 13.</dependency>

<!-- Then explicitly declare the version you want --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>32.1.3-jre</version> </dependency> ```

  1. 1.Use dependencyManagement to force a version:
  2. 2.```xml
  3. 3.<dependencyManagement>
  4. 4.<dependencies>
  5. 5.<!-- Forces this version for ALL transitive dependencies -->
  6. 6.<dependency>
  7. 7.<groupId>com.google.guava</groupId>
  8. 8.<artifactId>guava</artifactId>
  9. 9.<version>32.1.3-jre</version>
  10. 10.</dependency>
  11. 11.</dependencies>
  12. 12.</dependencyManagement>
  13. 13.`
  14. 14.Use Maven Enforcer Plugin to catch conflicts:
  15. 15.```xml
  16. 16.<plugin>
  17. 17.<groupId>org.apache.maven.plugins</groupId>
  18. 18.<artifactId>maven-enforcer-plugin</artifactId>
  19. 19.<version>3.4.1</version>
  20. 20.<executions>
  21. 21.<execution>
  22. 22.<id>enforce-dependency-convergence</id>
  23. 23.<goals><goal>enforce</goal></goals>
  24. 24.<configuration>
  25. 25.<rules>
  26. 26.<dependencyConvergence/>
  27. 27.</rules>
  28. 28.</configuration>
  29. 29.</execution>
  30. 30.</executions>
  31. 31.</plugin>

# Build fails if transitive dependencies have version conflicts # mvn validate will catch the issue before runtime ```

  1. 1.Use BOM for consistent version management:
  2. 2.```xml
  3. 3.<dependencyManagement>
  4. 4.<dependencies>
  5. 5.<!-- Spring Boot BOM manages versions for all Spring dependencies -->
  6. 6.<dependency>
  7. 7.<groupId>org.springframework.boot</groupId>
  8. 8.<artifactId>spring-boot-dependencies</artifactId>
  9. 9.<version>3.2.0</version>
  10. 10.<type>pom</type>
  11. 11.<scope>import</scope>
  12. 12.</dependency>
  13. 13.</dependencies>
  14. 14.</dependencyManagement>
  15. 15.`

Prevention

  • Run mvn dependency:tree after adding new dependencies
  • Use maven-enforcer-plugin with dependencyConvergence rule in CI
  • Pin transitive dependency versions in dependencyManagement
  • Use Spring Boot BOM or similar for framework dependencies
  • Add mvn validate as a CI step to catch conflicts before deployment
  • Use versions:display-dependency-updates to find outdated dependencies
  • Consider using Gradle which has better conflict resolution (picks highest version)