Introduction
Java ClassNotFoundException and NoClassDefFoundError occur when the JVM cannot locate or load a class at runtime, causing application failures ranging from startup crashes to intermittent runtime errors. ClassNotFoundException is thrown when code explicitly tries to load a class by name using Class.forName(), ClassLoader.loadClass(), or similar methods, but the class is not found on the classpath. NoClassDefFoundError occurs when a class was present during compilation but is missing at runtime, often due to missing JAR dependencies, classloader isolation, or failed static initializers. Common causes include missing JAR file on classpath, dependency version conflicts (JAR hell), classloader parent-child isolation preventing visibility, failed class initialization (ExceptionInInitializerError), package sealing violations, OSGi bundle export/import misconfiguration, application server classloader conflicts (server vs application classes), corrupted JAR file, and modular Java (JPMS) module path issues. The fix requires understanding Java's classloader architecture, classpath resolution, dependency management, and debugging tools. This guide provides production-proven troubleshooting for class loading errors across standalone applications, web containers, application servers, and modular applications.
Symptoms
java.lang.ClassNotFoundException: com.example.MyClassjava.lang.NoClassDefFoundError: com/example/MyClass- Application fails to start with
Caused by: java.lang.ClassNotFoundException - Method invocation fails with
NoClassDefFoundErrorfor dependency class Could not initialize classerror from failed static initializerClassCastExceptionfrom same class loaded by different classloaders- Application works in IDE but fails when packaged
- Intermittent errors as different classloaders attempt loading
ZipExceptionorInvalidJarExceptionfrom corrupted JAR- Module system errors with
java.lang.module.FindException
Common Causes
- JAR file missing from classpath or module path
- Dependency excluded transitively by build tool
- Classloader isolation (parent cannot see child classes)
- Static initializer throws exception (class marked as missing)
- Package sealed with different signer certificates
- OSGi bundle doesn't export required package
- Application server provides conflicting version of class
- Multi-release JAR version mismatch
- JPMS module not declared in module-info.java
- Shadow JAR / Uber JAR class relocation issues
Step-by-Step Fix
### 1. Diagnose class loading issues
Identify missing class location:
```bash # Find which JAR should contain the class jar -tf application.jar | grep "com/example/MyClass.class"
# Search all JARs in directory find . -name "*.jar" -exec jar -tf {} \; | grep "com/example/MyClass.class"
# Use jclasslib bytecode viewer # Download: https://github.com/ingokegel/jclasslib
# Check runtime classpath java -cp app.jar:/path/to/deps/* com.example.Main # Add -verbose:class to see class loading java -verbose:class -cp app.jar com.example.Main
# Output shows: # [Loaded java.lang.Object from /usr/lib/jvm/java-11/lib/rt.jar] # [Loaded com.example.MyClass from file:/path/to/app.jar] ```
Check classloader hierarchy:
```java // Print classloader hierarchy public class ClassLoaderDebug { public static void main(String[] args) { ClassLoader cl = ClassLoaderDebug.class.getClassLoader();
System.out.println("Current ClassLoader: " + cl);
int level = 0; while (cl != null) { System.out.println("Level " + level + ": " + cl); System.out.println(" Class: " + cl.getClass().getName()); if (cl instanceof URLClassLoader) { URLClassLoader ucl = (URLClassLoader) cl; for (URL url : ucl.getURLs()) { System.out.println(" URL: " + url); } } cl = cl.getParent(); level++; }
// Bootstrap classloader (null parent) System.out.println("Bootstrap ClassLoader: " + ClassLoader.getSystemClassLoader().getParent()); } } ```
### 2. Fix classpath configuration
Maven classpath:
```xml <!-- pom.xml - Ensure dependency is included --> <dependencies> <!-- Direct dependency --> <dependency> <groupId>com.example</groupId> <artifactId>my-library</artifactId> <version>1.0.0</version> </dependency>
<!-- Check for excluded dependencies --> <dependency> <groupId>org.example</groupId> <artifactId>some-lib</artifactId> <exclusions> <exclusion> <groupId>com.example</groupId> <artifactId>my-library</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
<!-- Check dependency tree --> <!-- Run: mvn dependency:tree -Dincludes=com.example:my-library -->
<!-- Build JAR with dependencies --> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>lib/</classpathPrefix> <mainClass>com.example.Main</mainClass> </manifest> </archive> </configuration> </plugin>
<!-- Copy dependencies to lib folder --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy-dependencies</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> ```
Gradle classpath:
```groovy // build.gradle dependencies { implementation 'com.example:my-library:1.0.0'
// Check for conflicts // Run: ./gradlew dependencies --configuration runtimeClasspath }
// Build fat JAR (all dependencies included) plugins { id 'com.github.johnrengelman.shadow' version '8.1.1' }
// Run: ./gradlew shadowJar // Output: build/libs/project-all.jar ```
Manual classpath setup:
```bash # Linux/Mac java -cp "app.jar:lib/*:config/" com.example.Main
# Windows java -cp "app.jar;lib/*;config/" com.example.Main
# With wildcard for multiple JARs java -cp "app.jar:lib/*" com.example.Main
# Explicit JAR list java -cp "app.jar:lib/mylib-1.0.jar:lib/otherlib-2.0.jar" com.example.Main ```
### 3. Fix dependency conflicts
Detect conflicting versions:
```bash # Maven dependency tree mvn dependency:tree -Dverbose -Dincludes=com.example:my-library
# Output shows: # +- org.parent:parent-lib:jar:1.0:compile # | \- com.example:my-library:jar:1.0.0:compile (version managed from 2.0.0) # +- com.other:other-lib:jar:1.0:compile # | \- com.example:my-library:jar:2.0.0:compile # # Conflict: 1.0.0 vs 2.0.0 - Maven selects nearest in tree
# Find which JARs are on classpath mvn dependency:build-classpath -Dmdep.outputFile=cp.txt cat cp.txt | tr ':' '\n' | sort ```
Resolve conflicts:
```xml <!-- Force specific version with dependencyManagement --> <dependencyManagement> <dependencies> <dependency> <groupId>com.example</groupId> <artifactId>my-library</artifactId> <version>2.0.0</version> <!-- Force this version --> </dependency> </dependencies> </dependencyManagement>
<!-- Or exclude conflicting transitive dependency --> <dependency> <groupId>org.parent</groupId> <artifactId>parent-lib</artifactId> <exclusions> <exclusion> <groupId>com.example</groupId> <artifactId>my-library</artifactId> </exclusion> </exclusions> </dependency>
<!-- Add explicit dependency --> <dependency> <groupId>com.example</groupId> <artifactId>my-library</artifactId> <version>2.0.0</version> </dependency> ```
Detect NoClassDefFoundError at runtime:
```java // Check if class is available public class ClassAvailability { public static boolean isClassAvailable(String className) { try { Class.forName(className, false, Thread.currentThread().getContextClassLoader()); return true; } catch (ClassNotFoundException e) { return false; } }
public static void main(String[] args) { String[] requiredClasses = { "com.example.RequiredClass", "org.apache.commons.lang3.StringUtils" };
for (String className : requiredClasses) { if (!isClassAvailable(className)) { System.err.println("Missing class: " + className); } } } } ```
### 4. Fix application server classloader issues
Tomcat classloader configuration:
```xml <!-- Tomcat classloader hierarchy: Bootstrap -> System -> Common -> Webapp -> JSP/EL
Classes in: - $CATALINA_HOME/lib: Visible to all webapps (Common) - WEB-INF/lib: Visible only to specific webapp - Shared lib: Configurable in catalina.properties -->
<!-- catalina.properties - Shared classloader --> shared.loader=${catalina.base}/shared/classes,${catalina.base}/shared/lib/*.jar
<!-- Server classloader (isolated from webapps) --> server.loader=${catalina.base}/server/classes,${catalina.base}/server/lib/*.jar
# Common issue: JAR in both WEB-INF/lib and catalina/lib # Solution: Remove from one location (prefer WEB-INF/lib) ```
WebLogic classloader:
```xml <!-- weblogic-application.xml - Application-level classloader --> <weblogic-application> <application-param> <param-name>weblogic.application.classloaders.enabled</param-name> <param-value>true</param-value> </application-param>
<!-- Filter classes to prevent conflicts --> <filter-application> <filter-name>my-filter</filter-name> <filter-class>weblogic.servlet.internal.ClassLoaderUtils</filter-class> </filter-application> </weblogic-application>
<!-- weblogic.xml - Web application classloader --> <weblogic-web-app> <container-descriptor> <prefer-web-inf-classes>true</prefer-web-inf-classes> <!-- true = Load from WEB-INF/lib first --> <!-- false = Load from server classpath first (default) --> </container-descriptor> </weblogic-web-app> ```
JBoss/WildFly classloader:
```xml <!-- jboss-deployment-structure.xml --> <jboss-deployment-structure> <deployment> <!-- Exclude server-provided module --> <exclusions> <module name="org.apache.commons.logging" /> <module name="org.apache.log4j" /> </exclusions>
<!-- Add explicit dependencies --> <dependencies> <module name="com.example.mymodule" services="import" /> </dependencies>
<!-- Classloader isolation --> <excluded-modules> <module name="javax.api" /> </excluded-modules> </deployment> </jboss-deployment-structure> ```
### 5. Fix static initializer failures
NoClassDefFoundError from failed initialization:
```java // WRONG: Static initializer fails, class marked as missing public class Database { private static final Connection CONNECTION;
static { // If this fails, entire class becomes unavailable CONNECTION = createConnection(); // Throws RuntimeException }
public static Connection getConnection() { return CONNECTION; // Never reached }
private static Connection createConnection() { throw new RuntimeException("Connection failed"); } }
// Usage: // Database.getConnection(); // NoClassDefFoundError! // Not ClassNotFoundException - class exists but failed initialization
// CORRECT: Handle initialization failure gracefully public class Database { private static final Connection CONNECTION; private static final Exception INIT_ERROR;
static { Connection conn = null; Exception error = null; try { conn = createConnection(); } catch (Exception e) { error = e; } CONNECTION = conn; INIT_ERROR = error; }
public static Connection getConnection() { if (INIT_ERROR != null) { throw new IllegalStateException("Database not initialized", INIT_ERROR); } return CONNECTION; } } ```
Debug static initializer:
java
// Catch initialization error
try {
Class.forName("com.example.Database");
} catch (ClassNotFoundException e) {
System.err.println("Class not found: " + e.getMessage());
} catch (NoClassDefFoundError e) {
System.err.println("Class failed to initialize: " + e.getMessage());
if (e.getCause() != null) {
System.err.println("Caused by: " + e.getCause());
e.getCause().printStackTrace();
}
} catch (ExceptionInInitializerError e) {
System.err.println("Static initializer failed: " + e.getMessage());
if (e.getCause() != null) {
e.getCause().printStackTrace();
}
}
### 6. Fix OSGi bundle classloading
OSGi bundle configuration:
```properties # bnd.bnd or pom.xml with bnd plugin
# Export packages for other bundles to use Export-Package: \ com.example.api;version="1.0.0", \ com.example.util;version="1.0.0"
# Import packages from other bundles Import-Package: \ org.apache.commons.lang3;version="[3.0,4.0)", \ com.other.bundle;version="[1.0,2.0)"
# Dynamic import (use sparingly) DynamicImport-Package: *
# Require specific bundles Require-Bundle: \ org.apache.felix.framework;version="[5.0,7.0)" ```
OSGi debugging:
```bash # Karaf console commands bundle:list # List all bundles bundle:classes <bundle-id> # Show classes in bundle bundle:info <bundle-id> # Show bundle details bundle:imports <bundle-id> # Show imported packages bundle:exports <bundle-id> # Show exported packages
# Check for unresolved bundles bundle:diag <bundle-id> # Show resolution errors
# Common OSGi errors: # - BundleException: Could not resolve module # - ClassNotFoundException: Package not exported # - NoClassDefFoundError: Bundle not started ```
### 7. Fix Java Platform Module System (JPMS) issues
Module configuration:
```java // module-info.java module com.example.myapp { // Declare dependencies on other modules requires java.sql; requires com.example.library;
// Export packages for other modules exports com.example.api;
// Open packages for reflection opens com.example.internal to com.example.test;
// Use services uses com.example.spi.MyService; provides com.example.spi.MyService with com.example.impl.MyServiceImpl; }
// Common errors: // - module not found: com.example.library // - package com.example.api is declared in module com.example but module com.other does not read it // - cannot access com.example.Internal: class file has restricted access ```
Module path setup:
```bash # Compile with module path javac -d out --module-path lib --module com.example.myapp
# Run with module path java -p out:lib --module com.example.myapp/com.example.myapp.Main
# Mixed classpath and module path (not recommended) java -cp app.jar -p lib --module com.example.myapp
# Migration from classpath: # 1. Add --add-modules for automatic modules # 2. Use --add-opens for reflection access # 3. Gradually migrate to explicit modules
java --add-modules ALL-MODULE-PATH \ --add-opens java.base/java.lang=ALL-UNNAMED \ -cp app.jar com.example.Main ```
### 8. Debug with advanced tools
Classloader debugging:
```bash # Enable classloader debug logging java -verbose:class -jar app.jar
# Enable JNI logging (for native class loading) java -Xcheck:jni -jar app.jar
# Enable module system debugging java --show-module-resolution -m com.example.myapp
# Trace class initialization java -XX:+TraceClassInitialization -jar app.jar ```
Arthas for runtime diagnosis:
```bash # Install Arthas (Alibaba Java diagnostic tool) # https://arthas.aliyun.com/
# Start Arthas java -jar arthas-boot.jar
# Commands: # sc -d com.example.MyClass # Show class details # sc -d *MyClass # Find class by pattern # classloader # List all classloaders # classloader -t # Show classloader hierarchy tree # classloader -c <hash> # Show URLs for classloader # jad com.example.MyClass # Decompile class # mc -c <hash> MyUtil.java # Compile and load new class ```
Prevention
- Use build tool dependency management (Maven/Gradle)
- Run
mvn dependency:treeorgradle dependenciesregularly - Test application with packaged JAR, not just IDE
- Configure application server classloader isolation properly
- Handle static initializer failures gracefully
- Document class loading requirements for library consumers
- Use shaded/fat JAR for standalone applications
- Validate JPMS module configuration before Java 9+ migration
- Implement class availability checks in application startup
Related Errors
- **ClassNotFoundException**: Explicit class load failed
- **NoClassDefFoundError**: Class missing at runtime (was present at compile)
- **ExceptionInInitializerError**: Static initializer failed
- **ClassCastException**: Same class loaded by different classloaders
- **UnsupportedClassVersionError**: Class compiled with newer Java version