What Just Happened
You deployed a JAR, ran a class, or started a server โ and the JVM immediately threw:
Exception in thread "main" java.lang.UnsupportedClassVersionError: com/example/MyClass has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 55.0
Java stamps a version number into every compiled .class file. When the JRE loading that file is older than the JDK that compiled it, you get this error. The JVM doesn't guess โ it refuses to load the class entirely.
Decode the Version Numbers
The error message includes a class file version. Map it to a Java release:
- 45.0 โ Java 1
- 52.0 โ Java 8
- 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
So class file version 61.0 means the class was compiled with Java 17, but the runtime only supports up to 55.0 (Java 11). Mismatch confirmed.
Diagnose: Find the Actual Versions
Check the running JRE
java -version
Example output:
openjdk version "11.0.20" 2023-07-18
OpenJDK Runtime Environment (build 11.0.20+8)
OpenJDK 64-Bit Server VM (build 11.0.20+8, mixed mode)
Check the JDK used to compile
javac -version
javac at 17, java at 11 โ that gap is your problem.
Inspect the class file directly
javap -verbose com/example/MyClass.class | grep "major version"
This prints the exact class file version without running the class.
On Linux: check all installed JDKs
update-alternatives --list java
# or
ls /usr/lib/jvm/
Fix Option 1 โ Upgrade the JRE (Recommended)
Can't recompile the JAR? That's typical with third-party libraries or CI-built artifacts. Upgrade the JRE on the target machine to match what compiled it.
Ubuntu / Debian
sudo apt install openjdk-17-jdk
sudo update-alternatives --config java
macOS (Homebrew)
brew install openjdk@17
export JAVA_HOME=$(brew --prefix openjdk@17)
export PATH="$JAVA_HOME/bin:$PATH"
Windows
Download and install the matching JDK from adoptium.net, then update JAVA_HOME and PATH in System Environment Variables.
Verify
java -version
# Should now show 17.x or higher
Fix Option 2 โ Recompile Targeting an Older Java Version
Own the source code? Use --release to compile down to the version your runtime supports.
Direct javac
javac --release 11 com/example/MyClass.java
Maven (pom.xml)
<properties>
<maven.compiler.release>11</maven.compiler.release>
</properties>
Or with the compiler plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
Gradle (build.gradle)
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
Gradle 6.7+ introduced toolchains โ the cleaner way to lock the version:
java {
toolchain {
languageVersion = JavaLanguageVersion.of(11)
}
}
Spring Boot projects
# application.properties or pom.xml property
<java.version>11</java.version>
Then rebuild:
./mvnw clean package
# or
./gradlew build
Fix Option 3 โ Point to the Correct JVM at Runtime
Multiple JDKs installed? Skip the system default entirely and pass the full path:
/usr/lib/jvm/java-17-openjdk-amd64/bin/java -jar myapp.jar
Or set JAVA_HOME before running:
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
java -jar myapp.jar
Handy in CI/CD pipelines or Docker containers where you don't want to touch system defaults.
Common Scenario: Docker Container with Wrong Base Image
Containerized apps hit this constantly. The Dockerfile uses openjdk:11 to run a JAR compiled with Java 17 โ the versions never matched to begin with.
# Wrong
FROM openjdk:11-jre-slim
COPY target/myapp.jar /app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
# Correct โ match the compile target
FROM eclipse-temurin:17-jre-alpine
COPY target/myapp.jar /app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
Verification Steps
- Confirm the JRE version matches or exceeds the compile target:``` java -version
- Recompiled? Check the new class file version:```
javap -verbose target/classes/com/example/MyClass.class | grep "major version"
- Run the application โ no
UnsupportedClassVersionErrormeans you're done:``` java -jar target/myapp.jar
- For Docker: rebuild and run the container, check startup logs.
## Lessons Learned
- **Pin Java versions in CI/CD.** Pipeline compiles with Java 21, production runs Java 11 โ this error hits every single deploy. Lock both to the same version and don't let them drift.
- **Use `--release` over `-source`/`-target`.** The old flags only set the bytecode version. They won't stop you from calling Java 17 APIs that don't exist in Java 11. `--release` enforces both the bytecode version and API availability.
- **Check third-party JARs.** The error doesn't always point to your code. Run `javap -verbose` on the class named in the stack trace to identify which dependency it came from, then check that library's minimum Java requirement.
- **Gradle toolchains and Maven wrapper eliminate the "works on my machine" problem.** They guarantee the build uses the declared Java version, regardless of what's installed locally.

