Fix: Java UnsupportedClassVersionError: Unsupported major.minor version
Quick Answer
How to fix Java UnsupportedClassVersionError caused by compiling with a newer JDK than the runtime, JAVA_HOME misconfiguration, and Maven/Gradle target version settings.
The Error
You run a Java application and get:
Exception in thread "main" java.lang.UnsupportedClassVersionError:
com/example/Main has been compiled by a more recent version of the Java Runtime (class file version 65.0),
this version of the Java Runtime only recognizes class file versions up to 61.0Or variations:
java.lang.UnsupportedClassVersionError: Unsupported major.minor version 52.0Error: LinkageError occurred while loading main class Main
java.lang.UnsupportedClassVersionError: Main (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 55.0The Java class file was compiled with a newer JDK than the JRE running it. The runtime does not understand the bytecode format.
Why This Happens
Each JDK version produces bytecode with a specific class file version number. A class compiled with JDK 21 (version 65.0) cannot run on JRE 17 (which only understands up to version 61.0). Java is forward-compatible (old code runs on new runtimes) but not backward-compatible (new code does not run on old runtimes).
Version mapping:
| Class Version | JDK Version |
|---|---|
| 52.0 | Java 8 |
| 53.0 | Java 9 |
| 55.0 | Java 11 |
| 56.0 | Java 12 |
| 57.0 | Java 13 |
| 58.0 | Java 14 |
| 59.0 | Java 15 |
| 60.0 | Java 16 |
| 61.0 | Java 17 |
| 62.0 | Java 18 |
| 63.0 | Java 19 |
| 64.0 | Java 20 |
| 65.0 | Java 21 |
| 66.0 | Java 22 |
| 67.0 | Java 23 |
Common scenarios:
- JAVA_HOME points to an old JDK. Your IDE or build tool compiled with a newer JDK than the one on your PATH.
- CI/CD uses a different JDK. The build server compiles with JDK 21 but the production server runs JDK 17.
- A dependency was compiled with a newer JDK. A third-party JAR targets a higher bytecode version.
- Docker base image has the wrong JDK. Your Dockerfile uses a JDK image for building but a different JRE image for running.
Fix 1: Check Your Java Versions
First, see which Java version is running your code:
java -versionThen see which Java version compiled the code:
javac -versionIf javac -version shows 21 but java -version shows 17, that is the problem. The compiler and runtime are different versions.
Check JAVA_HOME:
echo $JAVA_HOMEOn Windows:
echo %JAVA_HOME%Fix JAVA_HOME to point to the correct JDK:
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk
export PATH="$JAVA_HOME/bin:$PATH"Add this to your ~/.bashrc or ~/.zshrc to make it permanent.
Pro Tip: Use a Java version manager like
sdkmanto switch between JDK versions easily:sdk install java 21.0.2-open sdk use java 21.0.2-openThis sets
JAVA_HOMEandPATHautomatically without manual configuration.
Fix 2: Set the Target Version in Maven
Tell Maven to compile for a specific Java version, regardless of which JDK you use to compile:
Using the maven-compiler-plugin:
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>Or with the release flag (Java 9+, preferred):
<properties>
<maven.compiler.release>17</maven.compiler.release>
</properties>Full plugin configuration:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<release>17</release>
</configuration>
</plugin>
</plugins>
</build>The release flag is better than source/target because it also restricts the API to what is available in the target version. With source/target, you can accidentally use JDK 21 APIs that do not exist in JDK 17, and the code compiles but fails at runtime with NoSuchMethodError.
If Maven itself fails to resolve dependencies during the build, see Fix: Maven could not resolve dependencies.
Fix 3: Set the Target Version in Gradle
Kotlin DSL (build.gradle.kts):
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
tasks.withType<JavaCompile> {
options.release.set(17)
}Groovy DSL (build.gradle):
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
compileJava {
options.release = 17
}Rebuild after changing:
./gradlew clean buildIf Gradle build itself fails, see Fix: Gradle build failed.
Fix 4: Fix the Runtime JDK
Instead of changing the compiler target, upgrade the runtime to match the compiler:
Check what is installed:
# Linux
update-alternatives --list java
# macOS
/usr/libexec/java_home -VInstall the required JDK:
# Ubuntu/Debian
sudo apt install openjdk-21-jdk
# macOS (Homebrew)
brew install openjdk@21
# Windows (Chocolatey)
choco install openjdk21Set the new JDK as default:
# Linux
sudo update-alternatives --set java /usr/lib/jvm/java-21-openjdk-amd64/bin/java
# macOS
export JAVA_HOME=$(/usr/libexec/java_home -v 21)Fix 5: Fix Docker Multi-Stage Builds
A common mistake in Dockerfiles: compiling with one JDK version and running with another:
Broken:
# Build stage — JDK 21
FROM eclipse-temurin:21-jdk AS builder
WORKDIR /app
COPY . .
RUN ./gradlew build
# Run stage — JDK 17 (version mismatch!)
FROM eclipse-temurin:17-jre
COPY --from=builder /app/build/libs/app.jar /app.jar
CMD ["java", "-jar", "/app.jar"]Fixed — use the same major version:
# Build stage — JDK 21
FROM eclipse-temurin:21-jdk AS builder
WORKDIR /app
COPY . .
RUN ./gradlew build
# Run stage — JRE 21 (matches!)
FROM eclipse-temurin:21-jre
COPY --from=builder /app/build/libs/app.jar /app.jar
CMD ["java", "-jar", "/app.jar"]Always use the same major JDK version in both stages. Use the -jre variant for the runtime stage to keep the image small.
Fix 6: Fix Third-Party JAR Version Issues
If the error points to a third-party library class rather than your own code, the library was compiled for a newer JDK than your runtime.
Check a JAR’s target version:
javap -verbose -cp library.jar com.example.SomeClass | grep "major version"Or unzip the JAR and inspect the class files:
unzip -p library.jar META-INF/MANIFEST.MFFix options:
- Upgrade your runtime JDK to match the library’s requirement.
- Use an older version of the library that targets your JDK version. Check the library’s release notes for Java version requirements.
- Use the multi-release JAR if the library provides one. Multi-release JARs contain bytecode for multiple Java versions.
If a class is not found at all (rather than being the wrong version), see Fix: Java ClassNotFoundException.
Fix 7: Fix IDE-Specific Issues
IntelliJ IDEA:
- Go to File → Project Structure → Project
- Set Project SDK to the correct JDK version
- Set Project language level to match your target
- Go to File → Project Structure → Modules → Sources
- Set Language level for each module
- Go to Settings → Build → Compiler → Java Compiler
- Set Target bytecode version per module
Eclipse:
- Window → Preferences → Java → Compiler
- Set Compiler compliance level to your target
- Right-click project → Properties → Java Compiler
- Set project-specific compliance level
VS Code:
In settings.json:
{
"java.configuration.runtimes": [
{
"name": "JavaSE-17",
"path": "/usr/lib/jvm/java-17-openjdk",
"default": true
}
]
}Fix 8: Fix CI/CD Pipeline Versions
GitHub Actions:
steps:
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21' # Match your project's required versionMake sure the same version is used for both build and test steps. If your GitHub Actions workflow fails for other reasons, see Fix: GitHub Actions process completed with exit code 1.
Jenkins:
pipeline {
tools {
jdk 'JDK21'
}
}GitLab CI:
image: eclipse-temurin:21-jdk
build:
script:
- ./gradlew buildCommon Mistake: Setting the JDK version in the build step but forgetting to set it in the test and deploy steps. Each step might use a different default JDK. Always explicitly set the JDK version in every step that runs Java code.
Still Not Working?
If you have verified that the compiler and runtime are the same version:
Check for mixed dependencies. Your project might pull in a transitive dependency compiled for a newer JDK. Use mvn dependency:tree or gradle dependencies to inspect the full dependency tree.
Check for cached class files. Clean the build output completely:
# Maven
mvn clean
# Gradle
./gradlew clean
# Manual
rm -rf target/ build/ out/ bin/Old class files from a previous compilation might be lingering.
Check for fat JAR issues. If you use a shade plugin or shadow JAR, dependencies might include class files compiled for a different version. Inspect the JAR contents:
jar tf app.jar | head -20Check annotation processors. Annotation processors (Lombok, MapStruct, Dagger) run during compilation and might produce bytecode at a different level than your source code. Update them to versions compatible with your target JDK.
Check for bytecode manipulation libraries. Libraries like ASM, ByteBuddy, or cglib generate bytecode at runtime. If they target a version higher than the running JVM, you get this error. Update these libraries to versions that support your JDK.
If the error occurs as an OutOfMemoryError during compilation instead, see Fix: Java OutOfMemoryError for heap configuration.
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 ClassCastException: class X cannot be cast to class Y
How to fix Java ClassCastException by using instanceof checks, fixing generic type erasure, resolving ClassLoader conflicts, correcting raw types, and using pattern matching in Java 16+.
Fix: Java ConcurrentModificationException
How to fix Java ConcurrentModificationException caused by modifying a collection while iterating, HashMap concurrent access, stream operations, and multi-threaded collection usage.
Fix: Java NoSuchMethodError
How to fix Java NoSuchMethodError caused by classpath conflicts, incompatible library versions, wrong dependency scope, shaded JARs, and compile vs runtime version mismatches.
Fix: Java java.lang.IllegalArgumentException
How to fix Java IllegalArgumentException caused by null arguments, invalid enum values, negative numbers, wrong format strings, and Spring/Hibernate validation failures.