Skip to content
Latchkey

Java "UnsupportedClassVersionError" in CI - Runtime JDK Too Old

A class was compiled by a newer JDK than the JRE now trying to run it. The runtime reads the class-file version, sees it is from the future, and refuses to load it.

What this error means

The application or test launch fails immediately with UnsupportedClassVersionError, naming the class-file major version it found vs the maximum the runtime supports (e.g. 65.0 = Java 21, runtime supports up to 61.0 = Java 17).

JVM output
Exception in thread "main" java.lang.UnsupportedClassVersionError:
com/example/App 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

Common causes

Runtime JDK older than the compile JDK

The build compiled with JDK 21 (class version 65) but the deploy/test stage runs on JDK 17 (max 61). The older runtime cannot load the newer bytecode.

Mismatched JDKs across pipeline stages

Build and run stages use different setup-java versions or different base images, so the bytecode target outpaces the runtime.

How to fix it

Run on a JDK at least as new as the compile JDK

Provision the runtime JDK to match or exceed the version used to compile.

.github/workflows/ci.yml
- uses: actions/setup-java@v4
  with:
    distribution: temurin
    java-version: '21'   # runtime >= compile version

Or lower the bytecode target

If the runtime must stay on an older JDK, compile to that target with --release.

pom.xml / build.gradle.kts
# Maven
<maven.compiler.release>17</maven.compiler.release>
# Gradle
# java { toolchain { languageVersion = JavaLanguageVersion.of(17) } }

How to prevent it

  • Use the same JDK version for build and run stages, or run on one >= the compile target.
  • Pin --release to your true minimum runtime.
  • Assert the JDK version at the start of each pipeline stage.

Frequently asked questions

How do I map a class file version to a Java release?
Class file major version 52 is Java 8, 55 is Java 11, 61 is Java 17, 65 is Java 21, 67 is Java 23 - each later Java is one higher. The error shows what it found vs the runtime maximum.

Related guides

References

Latchkey auto-heals failures like this one - detected, fixed, and retried without you. Start free → 30-day trial · No credit card