The NoSuchMethodError you’re seeing means a Java application tried to call a method that it expected to find in a loaded class, but that method wasn’t there. This usually happens when the JVM is loading different versions of the same library, and the version it thinks it’s using doesn’t actually contain the method your code is looking for.
Here are the most common culprits and how to fix them:
1. Dependency Version Mismatch in Build Tools (Maven/Gradle)
This is by far the most frequent cause. Your project’s build file (e.g., pom.xml for Maven, build.gradle for Gradle) declares multiple dependencies that transitively pull in different versions of the same library. The build tool, by default, often picks one, but it might not be the one your code was compiled against.
Diagnosis:
- Maven: Run
mvn dependency:tree. Look for the problematic library (e.g.,log4j-core,guava) and see if multiple versions are listed. Pay attention to which version is being included by which direct dependency. - Gradle: Run
gradle dependencies. Similar to Maven, this will show the dependency tree and highlight version conflicts.
Fix:
-
Maven: Use an
<exclusion>tag in yourpom.xmlto remove the unwanted version. For example, if yourmy-appdepends onlibrary-A(which brings inlog4j-core:2.10.0) andlibrary-B(which brings inlog4j-core:2.17.1), and you want2.17.1everywhere:<dependency> <groupId>com.example</groupId> <artifactId>library-A</artifactId> <version>1.0</version> <exclusions> <exclusion> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.example</groupId> <artifactId>library-B</artifactId> <version>2.0</version> </dependency>Alternatively, use the
<dependencyManagement>section to enforce a specific version across all modules. -
Gradle: Use
resolutionStrategy.forcein yourbuild.gradle:configurations.all { resolutionStrategy { force 'org.apache.logging.log4j:log4j-core:2.17.1' } }
Why it works: This explicitly tells your build tool which version of the conflicting library to use, ensuring that only one version is packaged into your application’s artifact, and thus loaded by the JVM.
2. Application Server Classloader Hierarchy
If you’re deploying a Java EE application to an application server (like Tomcat, JBoss/WildFly, WebSphere), the server itself has its own set of libraries. Your application might also bundle its own copies of these libraries. The server’s classloader hierarchy determines which library gets loaded. If the server loads an older version and your app tries to use a method from a newer version it bundled, you’ll get NoSuchMethodError.
Diagnosis:
- Inspect the application server’s
libdirectory (e.g.,$CATALINA_HOME/libfor Tomcat). - Examine your application’s WAR/EAR file, specifically the
WEB-INF/liborlibdirectories. - Look for duplicate libraries (e.g.,
log4j-core.jar) in both locations.
Fix:
- Remove the conflicting library JAR from your application’s
WEB-INF/libdirectory. Let the application server provide the library. - Alternatively, if you must use a specific version bundled with your application, configure the application server to prioritize your application’s classloader over its own for specific libraries. This is highly server-specific and often involves modifying
catalina.properties(for Tomcat) or similar configuration files.
Why it works: This resolves the conflict by ensuring only one instance of the library is loaded, and that instance is the one the JVM expects. Removing it from your app means the server’s version is used; reconfiguring classloader priority means your app’s version is used.
3. OSGi Bundle Conflicts
In OSGi environments (like Eclipse Equinox, Apache Felix, or application servers that use OSGi internally), each bundle (a JAR with OSGi metadata) can import packages from other bundles. If two bundles import the same package but different versions, and the runtime resolves to the older version, you can hit this error.
Diagnosis:
- Use the OSGi console (
felix.shell.remoteor similar) to inspect the imported and exported packages for each bundle. - Look for the package containing the missing method and check which bundle version is providing it to the bundle that’s failing. Commands like
diagorinspectcan be useful.
Fix:
- Update the
Import-PackageorRequire-Bundledirectives in your bundle’sMETA-INF/MANIFEST.MFto specify the correct, newer version of the dependency. - Ensure the provider bundle for that package is also updated to the correct version.
- Use OSGi’s version range syntax carefully (e.g.,
my.package;version="[1.2, 2.0)") to manage dependencies.
Why it works: OSGi is all about managing dependencies explicitly. By correctly declaring the required package version in the manifest, you guide the OSGi framework to resolve the dependency to the correct providing bundle.
4. JAR Hell in Fat JARs/Uber JARs
When you create a "fat JAR" or "uber JAR" (a single JAR containing all application code and its dependencies), you can inadvertently include multiple versions of the same library if they were brought in by different direct dependencies. The order in which JARs are concatenated into the fat JAR can determine which version of a conflicting class is included.
Diagnosis:
- Unpack the fat JAR (it’s just a ZIP file).
- Search for the problematic library’s JAR file within the unpacked contents. You might find multiple instances.
- Use
jar -tf my-fat.jar | grep log4j-coreto list all occurrences.
Fix:
-
Use your build tool’s fat JAR plugin (e.g.,
maven-shade-plugin,maven-assembly-plugin, Gradle’sshadowJarplugin) to configure how conflicts are handled. Many have amergeServiceFilesorrelocationstrategy. -
The most common fix is to use the plugin to exclude all but one version of the conflicting JAR. For example, with
maven-shade-plugin:<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <minimizeJar>false</minimizeJar> <filters> <filter> <artifact>org.apache.logging.log4j:log4j-core</artifact> <includes> <include>org.apache.logging.log4j.LogManager.class</include> <!-- include other necessary classes --> </includes> </filter> </filters> <artifactSet> <excludes> <!-- Exclude all log4j-core except the one you want --> <exclude>org.apache.logging.log4j:log4j-core</exclude> </artifactSet> </artifactSet> </configuration> </execution> </executions> </plugin>A more robust approach is to use the
relocationfeature to rename packages of conflicting libraries if you cannot avoid them.
Why it works: This ensures that when the fat JAR is built, only a single, desired version of the library is included, or its classes are relocated to avoid namespace collisions.
5. Runtime Classpath Manipulation or Manual JAR Loading
If your application or script manually manipulates the Java classpath (-cp or -classpath arguments) or uses URLClassLoader to load JARs at runtime, it’s easy to introduce conflicting versions.
Diagnosis:
- Carefully examine the command line arguments used to start your application.
- Review any code that uses
URLClassLoaderor manipulatesSystem.getProperty("java.class.path"). - Print the classpath at runtime:
System.out.println(System.getProperty("java.class.path"));and inspect the JARs.
Fix:
- Clean up your command-line classpath. Remove duplicate or older versions of JARs.
- If using
URLClassLoader, ensure you are adding URLs to JARs in a consistent order and that you’re not adding multiple versions of the same library. A common pattern is to create aURLClassLoaderbased on the parent classloader and then add your specific JARs.
Why it works: Explicitly controlling the classpath ensures that the JVM loads the intended version of the library.
6. Conflicting Libraries in Plugin Architectures
If your application uses a plugin system (e.g., Jenkins plugins, Eclipse plugins, custom plugin frameworks), each plugin might bundle its own dependencies. This can lead to classpath conflicts similar to those in application servers or fat JARs, but managed by the plugin host.
Diagnosis:
- Identify which plugin is likely bringing in the conflicting library.
- Examine the plugin’s dependencies and its bundled JARs.
- Check the host application’s documentation for how it manages plugin classloaders and dependency resolution.
Fix:
- Configure the host application to enforce a specific version of the library for all plugins.
- If possible, update or recompile the problematic plugin to use the host’s provided version of the library or a compatible version.
- Use exclusion rules provided by the plugin host framework if available.
Why it works: This ensures that the plugin host’s classloader mechanism correctly resolves dependencies, preventing different plugins from loading incompatible library versions.
Once you’ve addressed these, you’ll likely run into a ClassNotFoundException if you’ve accidentally removed a library that was actually required by your code.