Skip to content

Fix: Java UnsupportedClassVersionError: Unsupported major.minor version

FixDevs ·

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.0

Or variations:

java.lang.UnsupportedClassVersionError: Unsupported major.minor version 52.0
Error: 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.0

The 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 VersionJDK Version
52.0Java 8
53.0Java 9
55.0Java 11
56.0Java 12
57.0Java 13
58.0Java 14
59.0Java 15
60.0Java 16
61.0Java 17
62.0Java 18
63.0Java 19
64.0Java 20
65.0Java 21
66.0Java 22
67.0Java 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 -version

Then see which Java version compiled the code:

javac -version

If 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_HOME

On 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 sdkman to switch between JDK versions easily:

sdk install java 21.0.2-open
sdk use java 21.0.2-open

This sets JAVA_HOME and PATH automatically 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 build

If 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 -V

Install the required JDK:

# Ubuntu/Debian
sudo apt install openjdk-21-jdk

# macOS (Homebrew)
brew install openjdk@21

# Windows (Chocolatey)
choco install openjdk21

Set 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.MF

Fix options:

  1. Upgrade your runtime JDK to match the library’s requirement.
  2. Use an older version of the library that targets your JDK version. Check the library’s release notes for Java version requirements.
  3. 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:

  1. Go to File → Project Structure → Project
  2. Set Project SDK to the correct JDK version
  3. Set Project language level to match your target
  4. Go to File → Project Structure → Modules → Sources
  5. Set Language level for each module
  6. Go to Settings → Build → Compiler → Java Compiler
  7. Set Target bytecode version per module

Eclipse:

  1. Window → Preferences → Java → Compiler
  2. Set Compiler compliance level to your target
  3. Right-click project → Properties → Java Compiler
  4. 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 version

Make 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 build

Common 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 -20

Check 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.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles