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
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 dependencyCommon 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.Find the conflicting dependency:
- 2.```bash
- 3.# Show full dependency tree
- 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.Exclude the older transitive version:
- 2.```xml
- 3.<dependency>
- 4.<groupId>com.example</groupId>
- 5.<artifactId>lib-a</artifactId>
- 6.<version>1.0</version>
- 7.<exclusions>
- 8.<exclusion>
- 9.<groupId>com.google.guava</groupId>
- 10.<artifactId>guava</artifactId>
- 11.</exclusion>
- 12.</exclusions>
- 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.Use dependencyManagement to force a version:
- 2.```xml
- 3.<dependencyManagement>
- 4.<dependencies>
- 5.<!-- Forces this version for ALL transitive dependencies -->
- 6.<dependency>
- 7.<groupId>com.google.guava</groupId>
- 8.<artifactId>guava</artifactId>
- 9.<version>32.1.3-jre</version>
- 10.</dependency>
- 11.</dependencies>
- 12.</dependencyManagement>
- 13.
` - 14.Use Maven Enforcer Plugin to catch conflicts:
- 15.```xml
- 16.<plugin>
- 17.<groupId>org.apache.maven.plugins</groupId>
- 18.<artifactId>maven-enforcer-plugin</artifactId>
- 19.<version>3.4.1</version>
- 20.<executions>
- 21.<execution>
- 22.<id>enforce-dependency-convergence</id>
- 23.<goals><goal>enforce</goal></goals>
- 24.<configuration>
- 25.<rules>
- 26.<dependencyConvergence/>
- 27.</rules>
- 28.</configuration>
- 29.</execution>
- 30.</executions>
- 31.</plugin>
# Build fails if transitive dependencies have version conflicts # mvn validate will catch the issue before runtime ```
- 1.Use BOM for consistent version management:
- 2.```xml
- 3.<dependencyManagement>
- 4.<dependencies>
- 5.<!-- Spring Boot BOM manages versions for all Spring dependencies -->
- 6.<dependency>
- 7.<groupId>org.springframework.boot</groupId>
- 8.<artifactId>spring-boot-dependencies</artifactId>
- 9.<version>3.2.0</version>
- 10.<type>pom</type>
- 11.<scope>import</scope>
- 12.</dependency>
- 13.</dependencies>
- 14.</dependencyManagement>
- 15.
`
Prevention
- Run
mvn dependency:treeafter adding new dependencies - Use
maven-enforcer-pluginwithdependencyConvergencerule in CI - Pin transitive dependency versions in
dependencyManagement - Use Spring Boot BOM or similar for framework dependencies
- Add
mvn validateas a CI step to catch conflicts before deployment - Use
versions:display-dependency-updatesto find outdated dependencies - Consider using Gradle which has better conflict resolution (picks highest version)