Comment résoudre java.lang.NoClassDefFoundError

L'erreur java.lang.NoClassDefFoundError fait partie des exceptions Java les plus frustrantes : le code compile sans problème, mais plante brutalement à l'exécution. Ce guide explique ses causes et propose une méthode systématique pour la résoudre.

Qu'est-ce que NoClassDefFoundError ?

C'est une erreur (et non une exception cochée) lancée par la JVM quand elle cherche à charger une classe qui existait à la compilation mais qu'elle ne trouve plus à l'exécution, ou dont l'initialisation a échoué.

Exception in thread "main" java.lang.NoClassDefFoundError: com/example/MaClasse
    at com.example.Application.main(Application.java:12)

Différence avec ClassNotFoundException

ErreurQuand ?Cause typique
ClassNotFoundExceptionChargement dynamique (Class.forName)Classe absente du classpath
NoClassDefFoundErrorRéférence statique dans le bytecodeClasse présente à la compilation mais absente au runtime, OU static initialisation échouée

Causes les plus fréquentes

1. Un JAR manquant dans le classpath

La cause n° 1. Le code compile parce que l'IDE voit le JAR, mais au lancement en ligne de commande la dépendance n'y est pas.

# Vérifier ce qui est réellement sur le classpath
java -cp target/app.jar:lib/* -verbose:class com.example.Application 2>&1 | head -30

2. Une initialisation statique qui échoue

Si un bloc static ou un champ static lève une exception au premier chargement de la classe, la JVM marque la classe comme erroneous. Toutes les références ultérieures déclenchent NoClassDefFoundError — mais la vraie cause est dans la stack trace précédente, souvent un ExceptionInInitializerError.

public class Config {
    // Si le fichier n'existe pas, cette ligne lève NullPointerException
    // La classe Config ne sera jamais chargeable
    static final Properties PROPS = loadProps("config.properties");
}

3. Un conflit de versions (JAR hell)

Deux versions d'une même bibliothèque dans le classpath. La première chargée gagne, et ses classes peuvent manquer des méthodes attendues par le reste du code.

mvn dependency:tree -Dverbose
./gradlew dependencyInsight --dependency slf4j-api

4. Une dépendance provided non fournie

En Maven, les dépendances marquées <scope>provided</scope> ne sont pas incluses dans le JAR exécutable. Normal pour les API servlet ou JDBC sur un serveur d'applications, problématique ailleurs.

5. Un chargeur de classes différent

Dans les serveurs d'applications (Tomcat, WildFly) ou OSGi, chaque déploiement a son propre ClassLoader. Une classe visible d'un module ne l'est pas forcément d'un autre.

Méthode de diagnostic en 4 étapes

  1. Lire toute la stack trace, y compris les Caused by: et les exceptions précédentes dans les logs. ExceptionInInitializerError juste avant indique une initialisation statique fautive.
  2. Identifier le JAR qui devrait contenir la classe. Utilisez jar tf mon.jar | grep MaClasse ou le moteur de recherche mvnrepository.com.
  3. Lister ce qui est sur le classpath :
    java -cp ... -XshowSettings:properties 2>&1 | grep java.class.path
  4. Vérifier les conflits avec mvn dependency:tree ou gradle dependencies.

Solutions concrètes

Avec Maven

<!-- Vérifiez que le scope n'est pas 'provided' pour un JAR exécutable -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.18.2</version>
</dependency>

Pour créer un fat JAR contenant toutes les dépendances :

<plugin>
  <artifactId>maven-shade-plugin</artifactId>
  <version>3.6.0</version>
</plugin>

Avec Gradle

dependencies {
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.2'
}
// Pour un fat jar :
tasks.jar { from(configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }) }

En ligne de commande

# Inclure explicitement tous les JAR du dossier lib/
java -cp "app.jar:lib/*" com.example.Application

Points de vigilance

  • Sur Windows, le séparateur de classpath est ; et non :.
  • Ne mélangez jamais des JAR du JDK et des JAR Java EE (problèmes classiques avec javax.servlet).
  • Pour une application Spring Boot, utilisez mvn spring-boot:run ou java -jar app.jar — jamais le JAR directement dans un classpath manuel.

Dans 90 % des cas, un simple mvn clean package suivi d'une vérification du target/app.jar suffit à résoudre le problème.