Fix: Java NoSuchMethodError
Part of: Java & JVM Errors
Quick Answer
How to fix Java NoSuchMethodError caused by classpath conflicts, incompatible library versions, wrong dependency scope, shaded JARs, and compile vs runtime version mismatches.
The Method That Vanished Between Compile and Runtime
Personally, this is the Java error I most consistently associate with build pipeline failures rather than code failures. The code compiled cleanly; somewhere between developer laptop and the running JVM, dependency resolution drifted, and a class that used to expose a method no longer does. I have learned to suspect the dependency tree before I suspect the call site. You run a Java application and get:
Exception in thread "main" java.lang.NoSuchMethodError:
'void com.google.gson.GsonBuilder.setStrictness(com.google.gson.Strictness)'Or variations:
java.lang.NoSuchMethodError: org.apache.commons.lang3.StringUtils.isBlank(Ljava/lang/CharSequence;)Zjava.lang.NoSuchMethodError: 'java.util.List com.example.UserService.findAll()'NoSuchMethodError: 'void org.slf4j.Logger.atDebug()'The JVM found the class but the method you are calling does not exist in it. The code was compiled against one version of a library but is running with a different version that does not have that method.
Quick Reference Before You Dive In
If you arrived here from Google with a fresh NoSuchMethodError, the five facts that resolve roughly 90 percent of cases:
- This is a dependency-version mismatch, NOT a code bug. The class is on the classpath; only the specific method version is wrong. The Java
NoSuchMethodErrorJavadoc and the JVM Spec Section 5.4.3.3 method resolution are the canonical references. - Run
mvn dependency:tree(or./gradlew dependencies) FIRST. Look for two versions of the same artifact. Maven picks the nearest in the tree; Gradle picks the highest by default. The mismatch is almost always there. - Force the correct version via
<dependencyManagement>(Maven) orresolutionStrategy.force(Gradle). Excluding the wrong version from one place leaves the conflict unresolved elsewhere. mvn clean package(not justmvn package) when dependencies change. Incremental builds can leave stale bytecode that references the old method shape.- For Spring Boot, override versions via BOM
<properties>, NOT direct version pins. Properties propagate to all related dependencies consistently; direct pins create new conflicts.
The rest of this article walks through each cause in detail, plus the failure modes most other guides skip.
Why the JVM Loads the Class but Cannot Find the Method
Java compiles method calls into bytecode that references the exact method signature (name, parameter types, return type). At runtime, the JVM loads the class and looks for that exact method. If the runtime class has a different version than the compile-time class, the method might not exist.
This is different from ClassNotFoundException (class not found at all) and NoClassDefFoundError (class found at compile time but missing at runtime). With NoSuchMethodError, the class is found but the specific method is missing.
Common causes:
- Dependency version conflict. Two libraries require different versions of the same dependency, and Maven/Gradle picks the wrong one.
- Stale compiled classes. You updated a dependency but did not recompile the code that uses it.
- Shaded/fat JAR conflict. A shaded JAR bundles an old version of a library that conflicts with a newer version on the classpath.
- Classpath ordering. The wrong version of a JAR is loaded first.
- JDK version mismatch. A method exists in JDK 17 but not in JDK 11.
The reason the JVM can run a class that is “wrong” is that bytecode references methods by their symbolic descriptor (package, class name, method name, parameter types, return type) but the actual resolution happens lazily at the call site, not at class load time. The class loader loads com.google.gson.GsonBuilder successfully because the class file exists. The method lookup only happens when your code actually calls setStrictness(...). If the version of GsonBuilder on the runtime classpath was compiled before that method existed (or with a different parameter type), the lookup fails with NoSuchMethodError. The error therefore points at the calling code, not at the library, which makes triage harder than it should be.
This is also why NoSuchMethodError is almost always a build pipeline failure, not a code failure. The code is fine: it compiled cleanly against one version of the library. The deploy artifact contains a different version. Somewhere between “developer’s laptop” and “production JVM,” dependency resolution drifted. That drift is the thing to fix, not the call site.
In Production: Incident Lens
NoSuchMethodError in production is one of the most embarrassing failure modes because it almost always means the build worked on someone’s machine but the production artifact is structurally wrong. The whole thing should have been caught before deployment.
- How it surfaces: Almost always at application startup. Spring Boot, Micronaut, and Quarkus apps throw it during bean initialization, the application context fails to start, and the process exits non-zero. In long-running apps it can also surface on the first request that exercises a particular code path: the class was not loaded until needed. The stack trace names the calling class and line, plus the missing method signature, which together pinpoint the conflicting dependency pair within minutes.
- Blast radius: 100% of pods running the broken image, immediately, unless a canary deploy gated the rollout. Because the error fires at startup, every replica crashes the same way at the same time. Without canary or rolling-deploy health gating, the orchestrator promotes the broken image globally before any human notices.
- What catches it: Container startup health checks (readiness/liveness probes), application startup time SLOs, and APM startup failures. Sentry/Rollbar may catch it if instrumentation initialized before the failing call, but most often the JVM dies before the error reporter flushes. The most reliable signal is “new replicas are not passing readiness”; that is your alert.
- Recovery sequence: Rollback the deploy immediately. Forward-fix is the wrong default here because the root cause is a dependency mismatch that is faster to debug locally than under deployment pressure. After rollback, reproduce by pulling the exact image and running it:
docker run --rm <image>should fail the same way. Then runmvn dependency:treeor./gradlew dependenciesagainst the committed lockfile to see which version actually ended up on the classpath. - Postmortem preventive: Three durable controls. First, add a dependency convergence check in CI (
maven-enforcer-pluginwithDependencyConvergence, GradlefailOnVersionConflict()). It fails the build when two transitive paths resolve different versions of the same artifact. Second, run a startup smoke test on the built artifact in CI (java -jar build/libs/app.jarwith--server.port=0and a quick readiness check). Third, canary deploy with health-check gating so a broken image cannot promote past one pod.
When to Use Which Fix
The next eight sections cover the fixes in detail. The table below maps your situation to the recommended fix.
| Your situation | Recommended fix | Why |
|---|---|---|
| Need to identify the version mismatch | Fix 1: dependency:tree, getProtectionDomain | Diagnose before fixing |
| Two versions on the classpath, want a specific one | Fix 2: BOM / <dependencyManagement> / resolutionStrategy.force | Tool-native pinning |
| Just bumped a dependency, error persists | Fix 3: mvn clean package / gradle clean build | Stale bytecode |
| Shaded / fat JAR conflicts with classpath | Fix 4: relocate shaded packages | Avoid collision |
| Method only exists in newer JDK | Fix 5: upgrade JDK or pick compatible API | JDK version split |
| Spring Boot BOM-managed conflict | Fix 6: override via property, not direct pin | Property propagates across related deps |
| Cannot tell which method signature is expected | Fix 7: reflection inspection at runtime | Verify actual class API |
| Test vs main classpath mismatch | Fix 8: check dependency scopes | Test-scoped class used in production |
If multiple rows apply, pick the topmost one.
Fix 1: Find the Conflicting Dependency
Identify which version of the library is actually being loaded at runtime:
Maven, show the dependency tree:
mvn dependency:treeLook for the library mentioned in the error. If you see multiple versions:
[INFO] +- com.example:my-app:jar:1.0
[INFO] | +- com.google.code.gson:gson:jar:2.8.9:compile
[INFO] +- com.other:library:jar:2.0
[INFO] | +- com.google.code.gson:gson:jar:2.10.1:compile (version managed from 2.8.9)Gradle, show dependencies:
./gradlew dependencies --configuration runtimeClasspathFind which JAR is loaded at runtime:
System.out.println(SomeClass.class.getProtectionDomain().getCodeSource().getLocation());This prints the JAR path that contains the class, revealing which version is loaded.
A habit I have built when triaging this error: confirm the RUNTIME classpath before doing anything else. The compile classpath that produced the bytecode is rarely the classpath that runs the app, and that gap is where this bug lives. -verbose:class shows you exactly which JAR provided which class:
java -verbose:class -jar app.jar 2>&1 | grep "SomeClass"This one command has saved me more time than any other diagnostic in the Java ecosystem.
Fix 2: Force the Correct Dependency Version
Maven, use <dependencyManagement>:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
</dependencies>
</dependencyManagement>This forces all transitive dependencies to use version 2.10.1.
Maven, exclude the wrong version:
<dependency>
<groupId>com.other</groupId>
<artifactId>library</artifactId>
<version>2.0</version>
<exclusions>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
</exclusions>
</dependency>Then add the correct version as a direct dependency.
Gradle, force a version:
configurations.all {
resolutionStrategy {
force("com.google.code.gson:gson:2.10.1")
}
}Gradle, exclude:
implementation("com.other:library:2.0") {
exclude(group = "com.google.code.gson", module = "gson")
}If Maven fails to resolve dependencies at all (rather than picking the wrong version), the issue is repository access or a missing artifact, not a method conflict.
Fix 3: Clean and Rebuild
Stale compiled classes can cause NoSuchMethodError when the dependency changed but your code was not recompiled:
Maven:
mvn clean packageGradle:
./gradlew clean buildIntelliJ IDEA:
Build → Rebuild Project (not just Build, which is incremental).
Delete build caches manually:
rm -rf target/ # Maven
rm -rf build/ # Gradle
rm -rf out/ # IntelliJ
rm -rf ~/.gradle/caches/ # Gradle global cache (nuclear option)A specific failure mode I have shipped more than once: running mvn package (without clean) right after a dependency version bump. The local class files do not get recompiled against the new JAR because their .class mtimes are newer than the source .java files. The resulting artifact references methods from the OLD library version. mvn clean package always after a dependency change; it is the cheapest insurance against this exact bug.
Fix 4: Check for Shaded JAR Conflicts
Shaded (fat/uber) JARs bundle dependencies inside them. If a shaded JAR contains an old version of a library, it conflicts with the correct version on the classpath:
Diagnose:
# List classes in a JAR
jar tf my-shaded-app.jar | grep "gson"If the shaded JAR contains com/google/gson/GsonBuilder.class, it is bundling Gson. The shaded copy might be an older version.
Fix: Relocate packages in the shade plugin:
Maven Shade Plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<relocations>
<relocation>
<pattern>com.google.gson</pattern>
<shadedPattern>shaded.com.google.gson</shadedPattern>
</relocation>
</relocations>
</configuration>
</plugin>Relocation moves the bundled classes to a different package, preventing conflicts with the non-shaded version.
Fix 5: Fix JDK Method Availability
Some methods only exist in newer JDK versions:
// Java 11+ only:
String result = " hello ".strip();
// Java 9+ only:
List<String> list = List.of("a", "b", "c");
// Java 16+ only:
record User(String name, int age) {}Check your runtime JDK version:
java -versionFix: Update the JDK to match the version the code was compiled for. See Fix: Java UnsupportedClassVersionError for JDK version management.
Fix: Use compatible alternatives for older JDKs:
// Instead of String.strip() (Java 11+):
String result = " hello ".trim(); // Java 1.0+
// Instead of List.of() (Java 9+):
List<String> list = Arrays.asList("a", "b", "c"); // Java 5+
List<String> list = Collections.unmodifiableList(Arrays.asList("a", "b", "c"));Fix 6: Fix Spring Boot Dependency Conflicts
Spring Boot manages dependency versions through its BOM (Bill of Materials). Overriding a managed version can cause NoSuchMethodError:
Check Spring Boot’s managed versions:
mvn help:effective-pom | grep "gson"Override safely with Spring Boot properties:
<properties>
<gson.version>2.10.1</gson.version>
</properties>Spring Boot property overrides are the safest way to change managed dependency versions because they update all related dependencies consistently.
Do NOT force incompatible versions:
If Spring Boot 3.x manages Jackson 2.15 and you force Jackson 2.10, Spring’s own code might call methods that only exist in 2.15. Stick to compatible version ranges.
Fix 7: Debug with Reflection
When the error message is unclear about which method is missing, use reflection to check:
import java.lang.reflect.Method;
for (Method method : SomeClass.class.getDeclaredMethods()) {
System.out.println(method);
}This lists all methods available in the class at runtime. Compare with what your code expects to find the mismatch.
Check the exact method signature:
try {
Method m = SomeClass.class.getMethod("methodName", String.class, int.class);
System.out.println("Found: " + m);
} catch (NoSuchMethodException e) {
System.out.println("Method not found!");
// List available methods for debugging
for (Method m : SomeClass.class.getMethods()) {
if (m.getName().contains("method")) {
System.out.println(" Available: " + m);
}
}
}Fix 8: Fix Test vs Runtime Classpath
A method might exist in the test classpath but not in the runtime classpath, or vice versa:
Maven scope issues:
<!-- This is only available during tests: -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>If a class from a test-scoped dependency is accidentally used in production code, it compiles but fails at runtime with NoSuchMethodError or ClassNotFoundException.
Fix: Check dependency scopes:
mvn dependency:tree -Dscope=runtime
mvn dependency:tree -Dscope=testCompare the two trees to find dependencies that are only in the test scope.
Stranger Causes I Have Tracked Down
Check for class loading order. In application servers (Tomcat, JBoss, WildFly), the class loading order differs from standalone applications. The server might load its own version of a library before yours.
Check for OSGi bundle issues. In OSGi environments (Eclipse, Karaf), each bundle has its own classloader. A method might be available in one bundle’s classloader but not another’s.
Check for annotation processor issues. Annotation processors (Lombok, MapStruct) generate code at compile time. If the processor version does not match the runtime library version, generated code might reference methods that do not exist.
Check for Java module system (JPMS) restrictions. In Java 9+, the module system can restrict access to methods. Add --add-opens or --add-exports flags if needed.
Check for SLF4J binding conflicts. Multiple SLF4J bindings on the classpath (Logback + Log4j + slf4j-simple) can cause NoSuchMethodError on org.slf4j.Logger.atDebug() and similar fluent API calls. The first binding loaded wins, and if it is an old version, newer methods are missing. Run mvn dependency:tree | grep slf4j and exclude the bindings you do not want.
Check for Lombok or annotation-processor version drift. Lombok 1.18.x generates code that calls JDK APIs at versions matching the build. If your CI uses JDK 17 with Lombok 1.18.20 but production runs JDK 11, generated code may reference methods missing on the runtime JVM. Pin Lombok to a version that supports your runtime JDK and run a smoke test on the produced JAR.
Check Kotlin standard library version. Kotlin code compiled against kotlin-stdlib:1.9.x can call methods that do not exist in kotlin-stdlib:1.8.x. If Spring Boot or another framework pulls in an older Kotlin stdlib via its BOM, your Kotlin code crashes at runtime. Force kotlin-stdlib to the version Kotlin compiled your code with, not the BOM default.
Check shaded JAR ordering on the classpath. When multiple shaded JARs are present, the JVM loads from whichever JAR appears first on the classpath. Run java -verbose:class -jar app.jar 2>&1 | grep GsonBuilder to see which JAR actually loaded the class. The fix is usually to relocate the shaded packages so they cannot collide.
What Other Tutorials Get Wrong About This Error
Most Java tutorials list the same fixes but frame them in ways that produce subtle bugs.
They focus on the calling code, not the dependency tree. The stack trace points at YOUR code, which is correct; but the bug lives in the dependency. Tutorials that ask you to “check the method signature” send readers staring at code that is fine. The fix is always upstream in the build graph.
They recommend excluding the conflicting version without forcing a replacement. <exclusions> removes one source of the dependency but does not set the version explicitly. If another path still requires the old version, you have moved the bug, not fixed it. Combine <exclusions> with <dependencyManagement> or use force to pin a single version.
They miss the Spring Boot BOM property override pattern. Trying to pin a Jackson version directly when Spring Boot’s BOM is managing it produces inconsistent transitive resolution. Spring Boot exposes properties (e.g., <jackson-bom.version>) that propagate the override to all related Jackson modules at once.
They omit -verbose:class as a diagnostic. This single JVM flag shows which JAR provided each class. Tutorials that focus on dependency:tree miss that the runtime classpath can still differ (shaded JARs, custom classloaders, application servers).
They miss the shaded-JAR relocation requirement. Bundling another library into a fat JAR without relocations causes the bundled copy to collide with the version your consumers have on their classpath. Articles that show how to BUILD a fat JAR without showing how to relocate the bundled packages produce libraries that silently break their users.
They miss the JPMS module-system case. In Java 9+, modules can hide methods even when the class is visible. --add-opens and --add-exports are needed for reflective access. Articles that pre-date the module system miss this entirely.
Frequently Asked Questions
What is the difference between NoSuchMethodError and NoSuchMethodException?
NoSuchMethodError is a runtime error from the JVM’s method-resolution machinery; it indicates a binary incompatibility between compiled bytecode and the loaded class. NoSuchMethodException is a checked exception thrown by Class.getMethod(...) when reflective lookup fails; it indicates a programming error in reflection code. The former is almost always a dependency mismatch; the latter is a bug in your reflective code.
Why does my code compile but fail at runtime?
The compiler resolved the method against the version of the library on the compile classpath; the JVM resolves it against the version on the runtime classpath. If those classpaths differ (different Maven scopes, shaded JARs, application server provides its own version), the method exists at compile time and is missing at runtime.
How do I find which JAR loaded a class?
System.out.println(SomeClass.class.getProtectionDomain().getCodeSource().getLocation());This prints the URL of the JAR (or directory) that the class came from. Combined with mvn dependency:tree, you can pinpoint the conflicting version.
Should I use <exclusions> or force?
Use <dependencyManagement> (Maven) or resolutionStrategy.force (Gradle) when you want one specific version everywhere. Use <exclusions> when you want to REMOVE a transitive dependency entirely (because something else provides it, or because you do not need it at all). They serve different purposes; in practice, force and BOM management cover most cases.
Why does my Spring Boot upgrade break Jackson calls?
Spring Boot’s BOM upgrades Jackson alongside the framework. If your code pins an older Jackson version directly, Spring’s internal code may call methods only in the newer version, producing NoSuchMethodError during bean initialization. The fix is to drop the direct version pin and let Spring’s BOM manage Jackson, or override via property if you need a specific newer version.
Is mvn dependency:tree enough, or should I use mvn dependency:resolve?
dependency:tree shows the resolved graph after Maven’s conflict resolution. dependency:resolve lists the final artifacts that will end up on the classpath. For diagnosing NoSuchMethodError, dependency:tree is usually enough; for verifying what actually ships in the JAR, dependency:resolve is more accurate.
If the class itself is not found (rather than a specific method), see Fix: Java ClassNotFoundException. If the JVM runs out of memory during class loading, see Fix: Java OutOfMemoryError.
For Gradle build failures that prevent the application from running, see Fix: Gradle build failed.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Java Record Not Working — Compact Constructor Error, Serialization Fails, or Cannot Extend
How to fix Java record issues — compact constructor validation, custom accessor methods, Jackson serialization, inheritance restrictions, and when to use records vs regular classes.
Fix: OpenTelemetry Not Working — Traces Not Appearing, Spans Missing, or Exporter Connection Refused
How to fix OpenTelemetry issues — SDK initialization order, auto-instrumentation setup, OTLP exporter configuration, context propagation, and missing spans in Node.js, Python, and Java.
Fix: Spring Boot Test Not Working — ApplicationContext Fails to Load, MockMvc Returns 404, or @MockBean Not Injected
How to fix Spring Boot test issues — @SpringBootTest vs test slices, MockMvc setup, @MockBean vs @Mock, test context caching, and common test configuration mistakes.
Fix: Spring Boot @Cacheable Not Working — Cache Miss Every Time or Stale Data
How to fix Spring Boot @Cacheable issues — @EnableCaching missing, self-invocation bypass, key generation, TTL configuration, cache eviction, and Caffeine vs Redis setup.