rea-reverse

Java class and JAR version checker

Find out which Java version a .class, JAR, WAR, EAR or Android AAR needs: the class file version of every class, nested jars included, the highest version per jar and package, multi-release layers, module-info and the build JDK from the manifest. Or paste an UnsupportedClassVersionError to have its numbers explained.

Your files never leave your browser. Archives are read locally in a Web Worker that has no network access. Nothing is uploaded. How to verify this yourself

Drop a .class, .jar, .war, .ear or .aar file here

or

Spring Boot fat jars (BOOT-INF/lib), WAR and EAR files, Android AARs (classes.jar and libs/) and ZIP64 archives are read too. Up to 1 GiB.

Explain an error message

UnsupportedClassVersionError, "Unsupported class file major version", javac "wrong version", "invalid target release", Kotlin JVM target and Android Gradle plugin Java errors. The text stays on this page.

Class file major version to Java version
Major versionBytes 6–7 (hex)Java versionLTS
7100 47Java 27
7000 46Java 26
6900 45Java 25LTS
6800 44Java 24
6700 43Java 23
6600 42Java 22
6500 41Java 21LTS
6400 40Java 20
6300 3fJava 19
6200 3eJava 18
6100 3dJava 17LTS
6000 3cJava 16
5900 3bJava 15
5800 3aJava 14
5700 39Java 13
5600 38Java 12
5500 37Java 11LTS
5400 36Java 10
5300 35Java 9
5200 34Java 8LTS
5100 33Java 7
5000 32Java 6
4900 31Java 5
4800 30Java 1.4
4700 2fJava 1.3
4600 2eJava 1.2
4500 2dJDK 1.0.2 / 1.1

From the Java Virtual Machine Specification (Java SE 27 edition, §4.1, major versions 45 to 71). Major version 45 covers JDK 1.0.2 and 1.1. Since Java 12, a minor version of 65535 marks a class that uses preview features of exactly that release.

What this tool does not do

  • It reads class file versions, not code: it cannot tell which JDK API a class calls, so a class built for Java 8 against a newer JDK without --release can still fail on Java 8 with NoSuchMethodError.
  • Nested archives are opened three levels deep (a jar in a war in an ear). Executable jars with a launch script in front of the ZIP data are not read.

Three error messages send people looking for a class file version:

  • 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
  • Unsupported class file major version 69 from Gradle, Spring, Byte Buddy, JaCoCo, or D8 and R8 in an Android build
  • class file has wrong version 61.0, should be 55.0 from javac

Each one names a number, and the number is the Java release that compiled the class: major version 65 is Java 21, 61 is Java 17, 52 is Java 8. What the message does not tell you is which class or dependency carries that number. This page reads every class in the file you give it and shows you.

What it does

Input. One file of up to 1 GiB:

  • a single .class file;
  • a JAR, including multi-release and modular jars, and shaded or “uber” jars with more than 65,535 entries (ZIP64);
  • a Spring Boot executable jar: the application classes in BOOT-INF/classes/ and every library in BOOT-INF/lib/;
  • a WAR (WEB-INF/classes/, WEB-INF/lib/) or an EAR with WARs and jars inside it;
  • an Android AAR: its classes.jar and the jars in libs/.

Nested archives are opened up to three levels deep, so a jar inside a WAR inside an EAR is still read. The file type is detected from the first bytes, not from its name.

Report.

  • Summary: the Java version the file needs, which is the highest class file version outside META-INF/versions/, plus the number of archives and classes, preview classes, Kotlin classes and modules.
  • Check against a Java runtime: pick Java 8, 11, 17, 21, 25 or any other release up to Java 27, and the tool lists every class a JVM of that version would refuse with UnsupportedClassVersionError, with the jar it sits in. Without a runtime it lists the classes that have the highest version.
  • Class file versions: how many classes have each major version, split into base classes, classes under META-INF/versions/ and preview classes.
  • Archives: one row per jar (the upload and each nested one) with its class count, highest version, version spread, flags (Multi-Release, module-info, Kotlin) and the MANIFEST.MF fields that say how it was built: Build-Jdk, Build-Jdk-Spec, Created-By, Multi-Release, Automatic-Module-Name, plus Implementation-Version, Main-Class and Start-Class when present.
  • Multi-release layers: for a multi-release JAR, the base layer and each META-INF/versions/N/ layer with its class count and highest version. A layer whose classes need a newer Java than N is flagged, and so are versioned classes in a jar whose manifest lacks Multi-Release: true (the JVM ignores them).
  • Modules: every module-info.class with the module name and version read from its Module attribute.
  • Classes using preview features: classes with minor version 65535.
  • Packages: the highest version per package and jar, filterable, 500 rows at a time.
  • Export: the whole report as JSON, every class with its major and minor version.

Error message explainer. Paste the error text and the tool picks out the numbers: UnsupportedClassVersionError in both its current and its Java 8 wording (“Unsupported major.minor version 52.0”), “Unsupported class file major version”, javac’s “wrong version” and “invalid target release” / “release version N not supported”, Kotlin’s “Cannot inline bytecode built with JVM target”, and the Android Gradle plugin’s “requires Java 17 to run”. javac’s Chinese-locale messages are recognised too: the translated wrong-version, release-not-supported and invalid target / source release errors. Each finding says what the numbers mean and what to change. When the message names the runtime’s version, one click checks the uploaded file against that runtime.

Reading the numbers

Bytes 4 to 7 of every class file are the minor and major version. The major version is the Java release plus 44 from Java 5 on: 49 is Java 5, 50 Java 6, 51 Java 7, 52 Java 8, and so on up to 71 for Java 27. Major 45 covers JDK 1.0.2 and 1.1; 46, 47 and 48 are 1.2, 1.3 and 1.4. The full table, from the Java Virtual Machine Specification (Java SE 27 edition, §4.1), is on this page above.

A JVM loads class files up to its own major version. Java 17 reads up to 61, so a class with 65 (Java 21) fails there with UnsupportedClassVersionError. Tools that read bytecode, such as ASM inside Gradle or D8 in an Android build, have their own ceiling: they fail with “Unsupported class file major version” on classes newer than the version their code knows, even when your JDK is new enough. That is why the same number can mean “your runtime is too old” in one message and “your build tool is too old” in another.

Since Java 12, minor version 65535 marks a class that uses preview features. Such a class loads only on exactly the Java release that compiled it, with --enable-preview. The tool tags those classes.

How it works

The page reads the ZIP central directory at the end of the file, then reads entry data in large Blob.slice blocks in file order. Local headers are parsed from those blocks, and each .class entry is inflated with the browser’s built-in DecompressionStream. When the classes of a jar add up to at most 64 MiB uncompressed, each class is inflated whole so the scan for the kotlin.Metadata annotation can run; above that, only the first few hundred compressed bytes of each class are inflated, enough for the 8-byte header. Nested jars stored without compression (as Spring Boot stores BOOT-INF/lib/) are read in place as a slice of the file; compressed ones (inside an AAR, WAR or EAR) are inflated into memory, up to 256 MiB each.

The ZIP reader is the one the APK analyzer uses, extended with ZIP64. Every count and size from the file is capped before it is used: 500,000 entries per archive, 1,000,000 classes in a report, three levels of nesting. A nested archive that is damaged, nested too deep or that inflates far beyond its compressed size is skipped and listed under Notes.

The .class files are not loaded, verified or run. The only bytes looked at are the 8-byte header, the Module attribute of module-info.class and, for Kotlin, the presence of the string Lkotlin/Metadata;.

Tested against javap. The test fixtures were compiled with JDK 11, 17 and 25 (Homebrew OpenJDK builds) using --release 8, 11, 17, 21 and 25 --enable-preview, then packed as a plain jar, a multi-release jar, a modular jar, a Spring Boot style jar, an AAR, an EAR containing a WAR, and a ZIP64 jar. For each of the 41 class entries in them, the major and minor version the tool reports equal what javap -v prints. The same check against javap passed on two real libraries from a Gradle cache: the androidx.core:core-ktx 1.13.1 AAR (130 classes, all flagged as Kotlin) and kotlin-stdlib 2.2.10 (970 classes, including META-INF/versions/9/module-info.class).

Large files. On an Apple M5 Pro, a 164 MB Spring Boot style jar built from 138 libraries (73,802 classes, all read whole, so Kotlin was counted) took 2.5 seconds in Chromium and 2.6 seconds in WebKit. A 207 MB shaded ZIP64 jar with 120,811 classes, where only the class headers are read, took 3.4 seconds in Chromium and 2.2 seconds in WebKit. Drawing the report adds up to four more seconds for 120,000 classes.

How your files stay private

Your file is not uploaded:

  • The archive is read in a Web Worker on your device, and the pasted error message is parsed on the page itself.
  • Class names, paths, module names and manifest values are shown as plain text; nothing from the file is interpreted as HTML or run.
  • Analytics events are coarse: success or an error code, the file type (jar, war, aar and so on), a class-count bucket, a size bucket and a duration bucket, and for the explainer only whether something was found. They never contain the file name, class or package names, versions, manifest values or the pasted text. The full list is on the privacy page.
  • The tool page shows no ads.

How to verify this yourself

  1. Open your browser’s developer tools and switch to the Network tab before you choose a file. Check a jar and paste an error. The only requests are page assets and the worker script /_astro/java-worker-*.js; none carries your file or the text.
  2. Or load the page, go offline (turn off Wi-Fi, or tick Offline in the Network tab) and check a file. It still works.
  3. The worker runs under the Content Security Policy default-src 'none'; script-src 'self'; connect-src 'none', so the browser does not let it make any network request.

Limitations

  • Versions, not APIs. A class compiled with -target 8 by a newer JDK, without --release 8, carries version 52 but may call methods that only exist in that newer JDK; it then fails on Java 8 with NoSuchMethodError, which this tool cannot predict.
  • No DEX. Classes already converted to DEX (an APK, a .dex file) have no class file version. For APKs use the APK analyzer.
  • Executable jars with a launch script (a shell script in front of the ZIP data, as in “fully executable” Spring Boot jars) are not read, nor are jars inside a jar more than three levels deep, encrypted entries, or compression methods other than STORED and DEFLATE.
  • Kotlin detection looks for the kotlin.Metadata annotation string in each class, so only Kotlin-compiled classes count (and any class that refers to that annotation). For jars with more than 64 MiB of classes, only the presence of META-INF/*.kotlin_module is reported.
  • Versions newer than Java 27 (major 72 and up) are shown as major minus 44 with a note, since those releases did not exist when this table was made.

FAQ

What Java version is class file version 61.0? 65.0?

61 is Java 17, 65 is Java 21, 69 is Java 25 (all three are long-term support releases), 55 is Java 11 and 52 is Java 8. Subtract 44 from the major version for Java 5 and later.

Gradle fails with “Unsupported class file major version 69”, but my code targets Java 17.

The class Gradle (or a plugin) could not read is often one of the JDK’s own classes: Gradle versions older than your JDK cannot run on it. Run Gradle on a JDK it supports and set the Java toolchain for compiling, or upgrade Gradle. If the class is in a dependency, drop that jar here to see its version.

Android D8 says “Unsupported class file major version”.

D8 in your Android Gradle plugin reads the class files of every dependency. Check the AAR or JAR named in the build log here: a library compiled for a newer Java than your AGP’s D8 supports needs either a newer AGP or an older version of that library.

My multi-release JAR has Java 17 classes, but the tool says it needs Java 8.

That is how multi-release jars work: Java 8 loads the base classes, and Java 17 and newer load the ones under META-INF/versions/17/ in their place. The summary uses the base layer and shows the versioned maximum separately.

Compliance

Check jars you built, open-source libraries or files you are authorized to inspect. Respect the licenses of the software you look at and the laws that apply to you. This site is not affiliated with Oracle, Google or the OpenJDK project. See the disclaimer.