ClassNotFoundException is a checked exception thrown during
explicit dynamic class loading (Class.forName(), ClassLoader.loadClass());
NoClassDefFoundError is an unchecked error thrown by the JVM when a class present at
compile time is missing at runtime. Both are ultimately classpath problems, but they require different
diagnostic approaches.mvn dependency:tree (Maven) or
gradle dependencies (Gradle) to find missing/conflicting dependencies, and
java -verbose:class to trace which classes the JVM loads and from where.NoClassDefFoundError caused by a failed static initializer
— the original ExceptionInInitializerError only appears on the first attempt;
subsequent calls show NoClassDefFoundError with no root cause.ClassNotFoundException is a checked exception
(extends ReflectiveOperationException extends Exception) that must be caught or declared;
NoClassDefFoundError is an error (extends LinkageError extends Error) that
should not be caught for retry logic — it indicates a broken runtime environment. [src1,
src2]
NoClassDefFoundError is caused by a failed static initializer, the original
ExceptionInInitializerError is thrown only on the first class load attempt. All subsequent
attempts throw NoClassDefFoundError with no cause chain — check logs for the initial
ExceptionInInitializerError. [src3, src5]exports or opens declarations in module-info.java. Adding
--add-opens or --add-exports JVM flags is a workaround, not a fix. [src1]
WEB-INF/lib vs server lib/) causes
ClassNotFoundException even when the JAR exists on disk. [src4, src5]NoClassDefFoundError in a catch block and continue execution — the
JVM's class resolution is cached, and the class will never load successfully in the same JVM session
once it has failed. [src2,
src3]| # | Cause | Likelihood | Signature | Fix |
|---|---|---|---|---|
| 1 | Missing dependency JAR | ~30% | ClassNotFoundException: com.example.SomeClass during Class.forName() or framework init |
Add the missing dependency to pom.xml/build.gradle or -cp [src4] |
| 2 | Dependency version mismatch | ~20% | NoClassDefFoundError: com/example/SomeClass — class existed at compile time but different JAR version at runtime |
Run mvn dependency:tree and resolve version conflicts [src4, src5] |
| 3 | Fat JAR packaging issue | ~15% | NoClassDefFoundError after mvn package / gradle build — dependencies not bundled |
Use maven-shade-plugin or spring-boot-maven-plugin; Gradle: shadowJar [src5] |
| 4 | Failed static initializer | ~10% | First: ExceptionInInitializerError; then repeated: NoClassDefFoundError: Could not initialize class X |
Fix the exception in the static block/field initializer [src3, src5] |
| 5 | Wrong classloader scope (app server) | ~8% | ClassNotFoundException in Tomcat/WildFly despite JAR on disk |
Move JAR to correct classloader scope (WEB-INF/lib vs server lib/) [src4] |
| 6 | Typo in fully qualified class name | ~5% | ClassNotFoundException: com.example.MyClss (misspelled) in Class.forName() |
Fix the class name string; Java is case-sensitive [src4, src6] |
| 7 | Missing transitive dependency | ~4% | NoClassDefFoundError for a class you never imported directly |
Run mvn dependency:tree -Dverbose to find excluded/missing transitive deps [src4] |
| 8 | JDBC driver not on classpath | ~3% | ClassNotFoundException: com.mysql.cj.jdbc.Driver |
Add JDBC driver dependency; modern drivers auto-register via SPI [src4] |
| 9 | Java module system (JPMS) visibility | ~2% | ClassNotFoundException or IllegalAccessError in Java 9+ modular apps |
Add requires/exports/opens in module-info.java; use --add-opens as workaround [src1] |
| 10 | Classpath ordering conflict | ~2% | Intermittent NoClassDefFoundError — class exists in multiple JARs |
Use mvn dependency:tree to identify duplicates; exclude unwanted JARs [src4, src5] |
| 11 | ProGuard/R8 removed class | ~1% | ClassNotFoundException after obfuscation/minification |
Add -keep rules for dynamically loaded classes [src4] |
START — ClassNotFoundException or NoClassDefFoundError?
│
├── ClassNotFoundException?
│ ├── Using Class.forName() / ClassLoader.loadClass()?
│ │ ├── YES → Check class name for typos; verify JAR on classpath (Cause #6, #1)
│ │ └── NO → Framework/container loading the class?
│ │ ├── YES → Check dependency scope (provided vs compile) and app server classloader (Cause #5)
│ │ └── NO → Check transitive dependencies: mvn dependency:tree (Cause #7)
│ └── JDBC driver class?
│ ├── YES → Add driver JAR; use SPI auto-registration (Cause #8)
│ └── NO → Verify exact FQCN and JAR presence on classpath
│
├── NoClassDefFoundError?
│ ├── Error contains "Could not initialize class"?
│ │ ├── YES → Failed static initializer (Cause #4)
│ │ │ └── Check logs for ExceptionInInitializerError; fix the static block
│ │ └── NO → Class missing at runtime but present at compile time
│ │ ├── Fat JAR? → Check packaging plugin (shade/spring-boot/shadow) (Cause #3)
│ │ ├── Dependency conflict? → mvn dependency:tree (Cause #2, #10)
│ │ └── App server? → Check classloader scope (Cause #5)
│ └── After ProGuard/R8?
│ ├── YES → Add -keep rules for affected classes (Cause #11)
│ └── NO → Run java -verbose:class to trace class loading
│
└── DEFAULT → Run java -verbose:class -cp ... MainClass and check which JARs are loaded
The error message tells you exactly which class is missing and whether it is a
ClassNotFoundException (checked exception, dynamic loading) or
NoClassDefFoundError (error, compile-time dependency missing at runtime). [src1,
src2]
# Search logs for the specific error
grep -E "ClassNotFoundException|NoClassDefFoundError|ExceptionInInitializerError" application.log
# Key patterns:
# java.lang.ClassNotFoundException: com.example.MyClass
# --> Dynamic loading failed (Class.forName, classloader)
# java.lang.NoClassDefFoundError: com/example/MyClass
# --> Class existed at compile time but missing/broken at runtime
# java.lang.ExceptionInInitializerError
# Caused by: <some RuntimeException>
# --> Static initializer failed; next load = NoClassDefFoundError
Verify: You have the exact fully qualified class name (e.g., com.example.MyClass) and the error type.
Use your build tool or manual search to identify the JAR that should provide the class. [src4]
# Maven: search for class in local repository
find ~/.m2/repository -name "*.jar" -exec sh -c \
'jar tf "$1" | grep -q "com/example/MyClass.class" && echo "$1"' _ {} \;
# Gradle: search dependency cache
find ~/.gradle/caches -name "*.jar" -exec sh -c \
'jar tf "$1" | grep -q "com/example/MyClass.class" && echo "$1"' _ {} \;
# Or use Maven Central search: https://search.maven.org/
Verify: You know the exact groupId:artifactId:version that contains the class.
Dependency version conflicts are the #1 cause in Maven/Gradle projects. A transitive dependency may pull in a different version that lacks the needed class. [src4, src5]
# Maven: full dependency tree with conflict markers
mvn dependency:tree -Dverbose
# Maven: check effective classpath
mvn dependency:build-classpath
# Gradle: full dependency tree
./gradlew dependencies --configuration runtimeClasspath
# Gradle: dependency insight for a specific module
./gradlew dependencyInsight --dependency commons-lang3 --configuration runtimeClasspath
Verify: mvn dependency:tree shows the expected version without (omitted for conflict) markers.
Based on what you found, add the missing dependency or resolve the version conflict. [src4, src5]
<!-- Maven: pom.xml -->
<dependency>
<groupId>com.example</groupId>
<artifactId>missing-library</artifactId>
<version>2.1.0</version>
</dependency>
<!-- Force version to resolve conflicts -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>conflicting-library</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>
</dependencyManagement>
// Gradle: build.gradle
dependencies {
implementation 'com.example:missing-library:2.1.0'
}
// Force a specific version
configurations.all {
resolutionStrategy {
force 'com.example:conflicting-library:3.0.0'
}
}
For standalone deployments (fat JARs), ensure all dependencies are bundled. [src5]
<!-- Maven: spring-boot-maven-plugin (Spring Boot apps) -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<!-- Maven: maven-shade-plugin (non-Spring apps) -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.1</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
// Gradle: shadow plugin for fat JARs
plugins {
id 'com.github.johnrengelman.shadow' version '8.1.1'
}
shadowJar {
archiveClassifier.set('')
mergeServiceFiles() // critical for SPI (JDBC drivers, etc.)
}
Verify: jar tf target/myapp.jar | grep com/example/MyClass.class confirms the class is in the packaged JAR.
If NoClassDefFoundError says "Could not initialize class", the real error is in a static block
or static field initializer. [src3, src5]
// BAD: static initializer that can fail at runtime
public class Config {
// NPE if env var missing → ExceptionInInitializerError then NoClassDefFoundError
private static final String VALUE = System.getenv("REQUIRED_VAR").toUpperCase();
}
// GOOD: defensive static initialization
public class Config {
private static final String VALUE;
static {
String env = System.getenv("REQUIRED_VAR");
VALUE = (env != null) ? env.toUpperCase() : "DEFAULT";
}
}
Verify: Search logs for the first ExceptionInInitializerError; fix the underlying exception, then restart the JVM.
// Input: Fully qualified class name as String
// Output: Loaded Class object or clear diagnostic error
public static Class<?> loadClassSafely(String className) {
try {
// Try Thread context classloader first (works in app servers)
return Thread.currentThread()
.getContextClassLoader()
.loadClass(className);
} catch (ClassNotFoundException e1) {
try {
// Fallback to Class.forName (uses caller's classloader)
return Class.forName(className);
} catch (ClassNotFoundException e2) {
throw new RuntimeException(
"Class '" + className + "' not found. " +
"Check: (1) JAR on classpath, (2) spelling, (3) version.",
e2);
}
}
}
<!-- Step 1: Run: mvn dependency:tree -Dverbose -Dincludes=com.google.guava -->
<!-- Step 2: Exclude the unwanted transitive version -->
<dependency>
<groupId>com.example</groupId>
<artifactId>library-a</artifactId>
<version>1.0.0</version>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Step 3: Declare the correct version explicitly -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.0.0-jre</version>
</dependency>
// Run: ./gradlew dependencyInsight --dependency guava --configuration runtimeClasspath
// Force a specific version globally
configurations.all {
resolutionStrategy {
force 'com.google.guava:guava:33.0.0-jre'
}
}
// Or exclude from a specific dependency
dependencies {
implementation('com.example:library-a:1.0.0') {
exclude group: 'com.google.guava', module: 'guava'
}
implementation 'com.google.guava:guava:33.0.0-jre'
}
// BAD — NoClassDefFoundError is cached by the JVM; the class will NEVER load [src2, src3]
for (int i = 0; i < 3; i++) {
try {
return new com.example.MyService();
} catch (NoClassDefFoundError e) {
System.out.println("Retry " + (i + 1) + "...");
Thread.sleep(1000); // Pointless — same error every time
}
}
// GOOD — fix the root cause, don't retry [src2, src3]
// 1. Add the missing JAR to the classpath
// 2. Restart the JVM (NoClassDefFoundError state is cached per session)
// 3. For static initializer failures, fix the static block and restart
// BAD — unnecessary since JDBC 4.0 (Java 6+); driver auto-registers via SPI [src4]
Class.forName("com.mysql.cj.jdbc.Driver");
Connection conn = DriverManager.getConnection(url, user, pass);
// GOOD — just ensure the driver JAR is on the classpath [src4]
// JDBC 4.0+ drivers register via META-INF/services/java.sql.Driver
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydb", "user", "pass");
<!-- BAD — 'provided' means the container supplies this [src4, src5] -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.0.0-jre</version>
<scope>provided</scope> <!-- Not provided by Tomcat/Jetty! -->
</dependency>
<!-- GOOD — compile scope includes the JAR in the packaged artifact [src4, src5] -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.0.0-jre</version>
<!-- scope defaults to 'compile' — included in WEB-INF/lib and fat JARs -->
</dependency>
<!-- Only use 'provided' for APIs truly supplied by the container (servlet-api, etc.) -->
ClassNotFoundException occurs during explicit dynamic loading
(Class.forName()); NoClassDefFoundError occurs when the JVM itself tries to
resolve a compile-time class reference. Different causes, different handling. [src3, src6]
provided vs compile: Dependencies with
<scope>provided</scope> are excluded from the packaged artifact. If the runtime
container does not supply the dependency, you get ClassNotFoundException. Only use
provided for APIs the container truly supplies (e.g., javax.servlet-api). [src4]ExceptionInInitializerError with the real exception. Every
subsequent attempt throws NoClassDefFoundError with no cause. Find the first error in logs.
[src3, src5]implementation vs api scope:
implementation dependencies are not exposed to downstream consumers. If module B depends on
module A which declares library X as implementation, B cannot see X's classes, leading to
NoClassDefFoundError. Use api for exposed classes. [src5]maven-shade-plugin or
shadowJar, SPI service files (META-INF/services/*) from multiple JARs can
overwrite each other. This breaks JDBC driver auto-registration, logging backends, etc. Enable
ServicesResourceTransformer (shade) or mergeServiceFiles() (shadow). [src5, src7]mvn package or gradle build. Always verify with the build tool's classpath.
[src4]WEB-INF/classes, WEB-INF/lib) before delegating to the parent. Placing a
dependency in $CATALINA_HOME/lib instead of WEB-INF/lib can cause version
conflicts or ClassNotFoundException. [src4]# === Trace class loading at runtime ===
java -verbose:class -cp "lib/*:myapp.jar" com.example.Main 2>&1 | grep "MyMissingClass"
# Shows: [Loaded com.example.MyMissingClass from file:/path/to/library.jar]
# === Print the effective classpath ===
# Maven:
mvn dependency:build-classpath -Dmdep.outputFile=classpath.txt
cat classpath.txt
# Gradle:
./gradlew dependencies --configuration runtimeClasspath
# === Search for a class in all JARs ===
for jar in lib/*.jar; do
jar tf "$jar" | grep -q "com/example/MyClass.class" && echo "FOUND in: $jar"
done
# === Check dependency tree for conflicts ===
mvn dependency:tree -Dverbose # Maven (verbose, shows conflict resolution)
mvn dependency:tree -Dincludes=com.google.guava # Maven (filter to specific dependency)
./gradlew dependencyInsight --dependency guava # Gradle (detailed resolution path)
# === Inspect a JAR's contents ===
jar tf target/myapp.jar | head -50
jar tf target/myapp.jar | grep "com/example/"
# === Check MANIFEST.MF for Class-Path entries ===
unzip -p target/myapp.jar META-INF/MANIFEST.MF
# === Check SPI service files (JDBC drivers, etc.) ===
jar tf target/myapp.jar | grep "META-INF/services"
unzip -p target/myapp.jar META-INF/services/java.sql.Driver
# === Java 9+ module system diagnostics ===
java --show-module-resolution -m com.example.myapp/com.example.Main
java --describe-module com.example.myapp
| Java Version | ClassLoader/Class Changes | Impact on ClassNotFoundException/NoClassDefFoundError |
|---|---|---|
| Java 1.0–7 | Single classpath, flat classloader | Standard classpath rules; -cp or CLASSPATH env var |
| Java 8 | Last version without module system | Most guides and Stack Overflow answers target this version |
| Java 9 | Module system (JPMS) introduced | Classes in non-exported packages are invisible across modules; --add-opens/--add-exports workaround |
| Java 11 (LTS) | javax.* packages removed from JDK |
ClassNotFoundException for javax.xml.bind.*, javax.annotation.* if not added as dependencies |
| Java 16 | Strong encapsulation of JDK internals | Reflection on JDK internal classes throws IllegalAccessError; use --add-opens |
| Java 17 (LTS) | Strongly encapsulated internals; no --illegal-access=permit |
Must use explicit --add-opens for each internal package |
| Java 21 (LTS) | Virtual threads; no new classloading changes | Same classloading behavior as Java 17 |
| Use This Guide When | Don't Use When | Use Instead |
|---|---|---|
java.lang.ClassNotFoundException in stack trace |
java.lang.ClassCastException (class exists, wrong type) |
Check for duplicate JARs from different classloaders |
java.lang.NoClassDefFoundError in stack trace |
java.lang.UnsupportedClassVersionError (wrong JDK version) |
Recompile with target JDK or upgrade runtime |
ExceptionInInitializerError followed by NoClassDefFoundError |
java.lang.OutOfMemoryError: Metaspace (too many classes) |
Java OutOfMemoryError guide |
| Class works in IDE but fails in packaged JAR | IncompatibleClassChangeError (method signature changed) |
Rebuild all JARs against same library version |
JDBC ClassNotFoundException: com.mysql.cj.jdbc.Driver |
java.sql.SQLException: No suitable driver (URL wrong) |
Check JDBC URL format matches driver |
ClassNotFoundException uses dots
(com.example.MyClass); NoClassDefFoundError uses slashes
(com/example/MyClass). CNFE reports the name as passed to Class.forName();
NCDFE reports the JVM internal name. [src6]
ClassNotFoundException for javax.xml.bind.JAXBContext,
javax.annotation.PostConstruct, etc. unless you add Jakarta replacements as explicit
dependencies. [src1]