Introduction

NoSuchMethodError at runtime means the class was compiled against one version of a dependency but a different version is present on the classpath at runtime. Maven's dependency resolution picks one version when multiple are declared transitively, and the chosen version may not have the method your code expects.

This is a classic "dependency hell" problem in Java, where compile-time and runtime classpaths diverge.

Symptoms

  • Application compiles successfully but throws NoSuchMethodError at runtime
  • Error shows "java.lang.NoSuchMethodError: com.example.Class.method(Ljava/lang/String;)V"
  • The method exists in the source code but not in the loaded JAR version

Common Causes

  • Two dependencies transitively pull in different versions of the same library
  • Maven picks the "nearest" version in the dependency tree, which may be older
  • A dependency was upgraded but its transitive dependencies still reference the old version

Step-by-Step Fix

  1. 1.Identify the conflicting dependency: Use dependency:tree to find all versions.
  2. 2.```bash
  3. 3.# Show full dependency tree:
  4. 4.mvn dependency:tree -Dverbose

# Search for a specific library: mvn dependency:tree | grep guava

# Output shows which dependency brings which version: # [INFO] +- com.google.guava:guava:jar:31.1-jre:compile # [INFO] - io.grpc:grpc-core:jar:1.50.0:compile # [INFO] - com.google.guava:guava:jar:30.1.1-android:compile (version managed from 31.1-jre) ```

  1. 1.Exclude the unwanted transitive version: Force the correct version with exclusions.
  2. 2.```xml
  3. 3.<dependency>
  4. 4.<groupId>io.grpc</groupId>
  5. 5.<artifactId>grpc-core</artifactId>
  6. 6.<version>1.50.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>

<!-- Declare the version you want explicitly --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.1-jre</version> </dependency> ```

  1. 1.Use dependencyManagement to enforce versions centrally: Control all transitive versions.
  2. 2.```xml
  3. 3.<dependencyManagement>
  4. 4.<dependencies>
  5. 5.<dependency>
  6. 6.<groupId>com.google.guava</groupId>
  7. 7.<artifactId>guava</artifactId>
  8. 8.<version>31.1-jre</version>
  9. 9.</dependency>
  10. 10.<dependency>
  11. 11.<groupId>com.fasterxml.jackson.core</groupId>
  12. 12.<artifactId>jackson-databind</artifactId>
  13. 13.<version>2.15.2</version>
  14. 14.</dependency>
  15. 15.</dependencies>
  16. 16.</dependencyManagement>
  17. 17.`
  18. 18.Use Maven Enforcer Plugin to detect conflicts: Fail the build on version conflicts.
  19. 19.```xml
  20. 20.<plugin>
  21. 21.<groupId>org.apache.maven.plugins</groupId>
  22. 22.<artifactId>maven-enforcer-plugin</artifactId>
  23. 23.<version>3.4.1</version>
  24. 24.<executions>
  25. 25.<execution>
  26. 26.<id>enforce</id>
  27. 27.<goals><goal>enforce</goal></goals>
  28. 28.<configuration>
  29. 29.<rules>
  30. 30.<dependencyConvergence/>
  31. 31.<requireUpperBoundDeps/>
  32. 32.</rules>
  33. 33.</configuration>
  34. 34.</execution>
  35. 35.</executions>
  36. 36.</plugin>
  37. 37.`

Prevention

  • Use BOM (Bill of Materials) to manage dependency versions consistently
  • Run mvn dependency:tree regularly to audit transitive dependencies
  • Enable maven-enforcer-plugin with dependencyConvergence in CI
  • Prefer Spring Boot's dependency management when using Spring ecosystem