How Do I Fix Java ClassNotFoundException and NoClassDefFoundError?

Type: Software Reference Confidence: 0.94 Sources: 7 Verified: 2026-02-23 Freshness: stable

TL;DR

Constraints

Quick Reference

# 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]

Decision Tree

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

Step-by-Step Guide

1. Read the full stack trace and identify the error type

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.

2. Find which JAR contains the missing class

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.

3. Check your dependency tree for conflicts

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.

4. Add or fix the dependency

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'
    }
}

5. Verify packaging includes all dependencies

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.

6. Fix static initializer failures (NoClassDefFoundError: Could not initialize class)

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.

Code Examples

Java: safe dynamic class loading with error handling

// 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);
        }
    }
}

Maven: diagnosing and fixing dependency conflicts

<!-- 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>

Gradle: diagnosing and fixing dependency conflicts

// 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'
}

Anti-Patterns

Wrong: Catching NoClassDefFoundError and retrying

// 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
    }
}

Correct: Fix the classpath and restart the JVM

// 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

Wrong: Using Class.forName() for JDBC drivers in modern Java

// 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);

Correct: Rely on JDBC 4.0 SPI auto-discovery

// 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");

Wrong: Using provided scope for runtime dependencies

<!-- 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>

Correct: Use compile (default) scope for application dependencies

<!-- 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.) -->

Common Pitfalls

Diagnostic Commands

# === 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

Version History & Compatibility

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

When to Use / When Not to Use

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

Important Caveats

Related Units