Java-fout "Unsupported class file major version" oplossen
Het getal in de fout verraadt alles: 52 is Java 8, 55 is Java 11, 61 is Java 17 en 65 is Java 21. Zo komt u erachter welke Java werkelijk draait en activeert u de juiste versie.
U start een Minecraft-server, een plug-in of een buildtool, en in plaats van de verwachte start verschijnt in de terminal een getal dat op het eerste gezicht niets zegt: class file version 65.0. Het goede nieuws is dat dit getal de diagnose al volledig bevat. U moet het alleen kunnen lezen. Dit artikel laat zien hoe u uit dat getal de benodigde Java-versie afleidt, hoe u erachter komt welke Java er werkelijk op uw server draait (vaak niet degene die u denkt), en hoe u de juiste versie blijvend activeert.
Twee verschillende foutmeldingen, twee verschillende oorzaken
De precieze formulering is doorslaggevend, want achter de twee gangbare meldingen gaan precies tegenovergestelde problemen schuil. De eerste komt van de Java Virtual Machine zelf:
Error: LinkageError occurred while loading main class net.minecraft.bundler.Main
java.lang.UnsupportedClassVersionError: net/minecraft/bundler/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
Deze melding betekent altijd hetzelfde: de applicatie is gebouwd met een nieuwere Java dan u hebt geïnstalleerd. Het eerste getal is wat de applicatie meebrengt, het tweede getal is wat uw runtime-omgeving begrijpt. In het voorbeeld hierboven vraagt de software om Java 21, terwijl Java 17 geïnstalleerd is.
De tweede variant ziet er vergelijkbaar uit, maar komt uit een heel andere hoek:
java.lang.IllegalArgumentException: Unsupported class file major version 65
Deze korte vorm komt niet van de JVM, maar van een bibliotheek die bytecode inleest en analyseert, doorgaans ASM. U ziet hem bij Gradle, bij oude loaders voor plug-ins en bij oudere servercores. Hier is de situatie meestal omgekeerd: uw Java is te nieuw voor de software die de bytecode wil lezen. Wie deze twee meldingen verwisselt, installeert in de verkeerde richting en vraagt zich vervolgens af waarom de fout blijft. Onthoud als vuistregel: staat er de lange zin met has been compiled by a more recent version, dan hebt u een nieuwere Java nodig. Staat er alleen de korte zin, dan hebt u vermoedelijk een oudere nodig.
Het getal ontcijferen: majorversie min 44
De omrekening is eenvoudiger dan de meeste handleidingen doen voorkomen. De class-file-majorversie min 44 geeft de Java-versie. 65 min 44 is 21, klaar. De regel geldt zonder uitzondering vanaf Java 1.1 en verandert ook in de toekomst niet, omdat elke nieuwe Java-hoofdversie het getal met precies één verhoogt.
| Class file version | Java-versie | Waar u het tegenkomt |
|---|---|---|
| 52 | Java 8 | Minecraft tot en met 1.16.5, oude Forge-modpacks, oudere enterprisesoftware |
| 53 | Java 9 | zelden, overgangsversie |
| 55 | Java 11 | veel bibliotheken, oudere Spring-applicaties |
| 60 | Java 16 | Minecraft 1.17.x |
| 61 | Java 17 | Minecraft 1.18 tot 1.20.4, zeer veel actuele plug-ins |
| 65 | Java 21 | Minecraft vanaf 1.20.5, dus de hele 1.21-reeks, moderne servercores |
| 69 | Java 25 | gloednieuwe builds, actuele LTS-release |
De tussenliggende waarden volgen dezelfde regel: 62 is Java 18, 63 is Java 19, 64 is Java 20, 66 is Java 22 enzovoort. De .0 achter het getal is de minorversie en speelt praktisch nooit een rol.
Wat dat concreet betekent voor Minecraft
De toewijzing voor Minecraft-servers is eenduidig en makkelijk uit het hoofd te leren. Tot en met 1.16.5 geldt Java 8, voor 1.17.x minstens Java 16, van 1.18 tot 1.20.4 minstens Java 17, en vanaf 1.20.5 (en daarmee voor de hele 1.21-reeks) Java 21. Duikt class file version 65.0 dus op in het logbestand kort nadat u naar een actuele versie hebt bijgewerkt, dan is de oorzaak al gevonden voordat u ook maar naar de configuratie kijkt.
Welke Java draait er werkelijk?
De meest gemaakte denkfout bij dit soort fouten is de aanname dat de uitvoer van java -version in uw SSH-sessie ook geldt voor de draaiende service. Dat klopt vaak niet. Begin er toch maar mee:
java -version
De eerste regel van de uitvoer noemt de versie, bijvoorbeeld openjdk version "21.0.11" 2026-04-15. Verschijnt in plaats daarvan bash: java: command not found, dan staat er helemaal geen Java in het zoekpad en wordt de applicatie door een startscript met een absoluut pad gestart. Kijk na welke runtimes er geïnstalleerd zijn:
ls /usr/lib/jvm
readlink -f "$(command -v java)"
update-alternatives --display java
readlink -f lost de keten van symlinks op en toont u de binary die werkelijk wordt uitgevoerd, bijvoorbeeld /usr/lib/jvm/java-17-openjdk-amd64/bin/java. Dat is belangrijker dan het versienummer alleen, want dit pad hebt u later nodig.
De middelste regel gebruikt bewust command -v en niet het meer verbreide which. which is een apart programma en zit niet in de minimale installatie van AlmaLinux 9 en 10, Rocky Linux 9 en Oracle Linux 9. Daar loopt de which-variant tweemaal vast: eerst which: command not found, daarna readlink: missing operand. command -v zit in de shell zelf en werkt overal.
Voor een proces dat al draait is er een manier die geen enkele twijfel overlaat. Die beantwoordt de vraag met welke Java de service werkelijk is gestart, los van zoekpad en omgevingsvariabelen:
ls -l /proc/$(pgrep -f server.jar | head -n 1)/exe
De symlink exe wijst naar het uitvoerbare bestand van het proces. Staat daar een ander pad dan verwacht, dan hebt u de oorzaak te pakken: de service start met een andere Java dan uw shell. Dat gebeurt regelmatig bij systemd-units, want die brengen hun eigen minimale omgeving mee en uw JAVA_HOME uit de .bashrc bestaat daar simpelweg niet.
De classversie rechtstreeks uit het JAR-bestand lezen
Soms wilt u weten welke Java een bestand vereist voordat u het ook maar start. Dat kan zonder extra gereedschap te installeren. Elk .class-bestand begint met de signatuur CAFEBABE, gevolgd door twee bytes minorversie en twee bytes majorversie. Het achtste byte is dus het getal dat u zoekt:
unzip -p server.jar net/minecraft/bundler/Main.class | od -An -tu1 -N8
De uitvoer luidt dan bijvoorbeeld 202 254 186 190 0 0 0 65. De eerste vier getallen vormen de signatuur, het laatste is uw antwoord: 65, dus Java 21. Bij andere programma's past u het pad naar de klasse aan. De juiste naam levert unzip -l server.jar of het item Main-Class in META-INF/MANIFEST.MF. Is er een JDK geïnstalleerd, dan kan het ook comfortabeler:
javap -verbose -cp server.jar net.minecraft.bundler.Main | grep major
Bij plug-ins is deze truc bijzonder nuttig. Breekt één enkele plug-in de serverstart af, controleer dan de hoofdklasse ervan. U weet dan meteen of de plug-in te nieuw is voor uw servercore of juist andersom.
De juiste Java-versie installeren
Hier lopen de distributies duidelijk uiteen, en algemene handleidingen stranden precies op dit punt. De stand van zaken op echte systemen, gecontroleerd in juli 2026:
| Pakket | Debian 13 | Debian 12 | Ubuntu 24.04 | Ubuntu 22.04 |
|---|---|---|---|---|
| openjdk-8-jre-headless | niet aanwezig | niet aanwezig | 8u492 | 8u492 |
| openjdk-11-jre-headless | niet aanwezig | niet aanwezig | 11.0.31 | 11.0.31 |
| openjdk-17-jre-headless | niet aanwezig | 17.0.19 | 17.0.19 | 17.0.19 |
| openjdk-21-jre-headless | 21.0.11 | niet aanwezig | 21.0.11 | 21.0.11 |
| openjdk-25-jre-headless | 25.0.3 | niet aanwezig | 25.0.3 | 25.0.3 |
Neem deze tabel één keer rustig door, dat scheelt u veel tijd. Debian 12 kent uitsluitend Java 17, Debian 13 kent Java 17 niet meer en levert alleen 21 en 25. Wie dus op Debian 12 een Minecraft-server uit de 1.21-reeks wil draaien, of op Debian 13 een programma met class file version 61.0, vindt in de standaardpakketbronnen niets passends. Ubuntu is op dit punt ruimhartiger en levert alles van Java 8 tot en met 25 uit de eigen pakketbronnen.
Controleer vóór het installeren of het pakket wel beschikbaar is:
apt update
apt-cache policy openjdk-21-jre-headless
Staat er bij Kandidaat of Candidate een (geen), dan bestaat het pakket in uw versie niet. Anders installeert u het gewoon. Om een applicatie alleen uit te voeren volstaat de JRE, en het -headless-pakket bespaart u de grafische afhankelijkheden. Op een server is dat altijd de juiste keuze:
apt install -y openjdk-21-jre-headless
Wie zelf compileert of Gradle dan wel Maven gebruikt, heeft de JDK nodig in plaats van de JRE, dus openjdk-21-jdk-headless. Uitgebreidere routes vindt u in onze artikelen over Java 17 op Debian en Java 21 op Debian.
Als de distributie de versie niet aanbiedt: Adoptium Temurin
Voor alle gevallen die de standaardpakketbronnen niet dekken, is er de pakketbron van Adoptium. Die levert Temurin 8 tot en met 26 voor Debian 12, Debian 13, Ubuntu 22.04 en Ubuntu 24.04 en is daarmee de enige oplossing die op elk van deze systemen elke benodigde versie beschikbaar stelt. Belangrijk: de vroeger gebruikelijke route via apt-key is afgeschaft. De sleutel hoort tegenwoordig in /etc/apt/keyrings/ en wordt met signed-by aan precies deze ene pakketbron gekoppeld.
apt install -y wget gpg apt-transport-https
mkdir -p /etc/apt/keyrings
wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor | tee /etc/apt/keyrings/adoptium.gpg > /dev/null
echo "deb [signed-by=/etc/apt/keyrings/adoptium.gpg] https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list
apt update
apt install -y temurin-21-jdk
De awk-aanroep vult automatisch de juiste codenaam in, dus trixie, bookworm, noble of jammy. Breekt apt update daarna af met Conflicting values set for option Signed-By, dan bestaat er al een oudere Adoptium-pakketbron, meestal aangelegd via extrepo. Zoek die op in /etc/apt/sources.list.d/ en verwijder het dubbele bestand.
AlmaLinux, Rocky Linux en RHEL
In de Red Hat-familie heten de pakketten anders en dragen zij de versie in de naam, zonder openjdk-prefix vooraan:
dnf install -y java-21-openjdk-headless
AlmaLinux 9, Rocky Linux 9 en Oracle Linux 9 voeren de volledige reeks van java-1.8.0-openjdk-headless tot java-25-openjdk-headless. AlmaLinux 10 heeft daarentegen dezelfde knip gemaakt als Debian 13 en kent alleen nog 21 en 25. Een dnf install java-17-openjdk-headless eindigt daar met No match for argument.
Ook het gereedschap om te wisselen heet hier gewoon alternatives, en update-alternatives is daar alleen maar een symlink naartoe. Het gedraagt zich echter niet hetzelfde: alternatives --list neemt geen argument. Het van Debian bekende update-alternatives --list java geeft daar alleen de helptekst en eindigt met retourcode 2. Gebruik alternatives --list | grep java of alternatives --display java. Dat laatste meldt dan java - status is auto. in plaats van de Debian-vorm java - auto mode.
De installatiepaden dragen in de Red Hat-familie de volledige pakketversie in de mapnaam, dus bijvoorbeeld /usr/lib/jvm/java-21-openjdk-21.0.11.0.10-1.el9.x86_64, en niet de korte Debian-vorm met architectuursuffix. Alleen AlmaLinux 10 legt daarnaast de korte symlink java-21-openjdk aan. Neem het pad dus niet klakkeloos over, maar haal het op met alternatives --display java.
Eén gedrag is hier bijzonder gemeen: na de installatie van Java 17 naast een al aanwezige Java 21 zette alternatives op AlmaLinux 9 de link /usr/bin/java eigenhandig om naar de oudere versie 17. Wie de tweede versie er dus alleen bij installeert om die gericht voor een oude applicatie te gebruiken, verandert daarmee onbedoeld de standaard-Java van het hele systeem. Zet na elke extra installatie uitdrukkelijk alternatives --set java <pad> en controleer het resultaat met java -version.
De juiste versie activeren
Geïnstalleerd betekent niet actief. Na de installatie van een tweede JDK toont java -version vaak nog steeds de oude versie, omdat de symlink /usr/bin/java onveranderd blijft. Op Debian en Ubuntu regelt het alternatives-mechanisme dat:
update-alternatives --list java
update-alternatives --config java
Het tweede commando toont een genummerde lijst en vraagt om uw keuze. Daarna staat de keuze op manual en wordt die door toekomstige pakketinstallaties niet meer overschreven, precies zoals het bedoeld is. Wie dit zonder tussenvraag wil scripten, gebruikt de set-variant met het volledige pad:
update-alternatives --set java /usr/lib/jvm/temurin-21-jdk-amd64/bin/java
Stel daarnaast JAVA_HOME in wanneer er buildtools in het spel zijn. Gradle en Maven negeren de symlink en richten zich naar deze variabele:
export JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64
$JAVA_HOME/bin/java -version
Houd er rekening mee dat een export alleen geldt in de lopende sessie. Voor services helpt dat helemaal niets: na de volgende herstart pakt de service weer de oude Java-versie, omdat hij de variabele nooit heeft gezien. Blijvend hoort JAVA_HOME daarom in de systemd-unit als Environment= of, systeembreed, in /etc/environment.
De belangrijkste stap bij services
Draait uw applicatie als systemd-service, dan is de alternatives-symlink maar het halve werk. Een service start met een eigen omgeving, en staat daar een oud absoluut pad in ExecStart, dan verandert geen enkele update-alternatives ter wereld daar nog iets aan. Vul het volledige pad in, zodat de versie onafhankelijk van de systeemtoestand vastligt:
[Service]
Environment="JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64"
ExecStart=/usr/lib/jvm/temurin-21-jdk-amd64/bin/java -Xms4G -Xmx4G -jar server.jar nogui
Lees de configuratie daarna beslist opnieuw in, anders blijft de oude definitie draaien:
systemctl daemon-reload
systemctl restart minecraft
Hoe zo'n unit er volledig uitziet, laten onze artikelen systemd-service aanmaken en Minecraft-server automatisch starten zien. Dezelfde valkuil geldt voor startscripts die via screen of tmux draaien, en voor panels zoals Pterodactyl, die de Java-versie in de container-image vastleggen en niet op het hostsysteem.
Het omgekeerde geval: Java is te nieuw
Duidelijk zeldzamer, maar verwarrender, is het andere geval. U start een ouder modpack of een oude buildtool op een vers opgezette server met Java 21 en krijgt de korte zin Unsupported class file major version 65 te zien, terwijl toch alles actueel is. Hier meldt een bibliotheek dat zij niets kan met het bytecodeformaat van uw nieuwe runtime-omgeving. Typische veroorzakers zijn Forge-modpacks voor 1.12.2, oudere Gradle-versies en flink verouderde loaders voor plug-ins.
De oplossing is niet om de nieuwe Java te verwijderen. Installeer in plaats daarvan de oude versie ernaast en spreek die gericht aan met een absoluut pad. Precies daarvoor is het alternatives-mechanisme gebouwd, en precies daarom loont het om Java 8 en Java 21 tegelijk op één systeem te houden. Op Ubuntu kan dat rechtstreeks uit de standaardpakketbronnen, op Debian via Temurin:
apt install -y temurin-8-jdk
/usr/lib/jvm/temurin-8-jdk-amd64/bin/java -version
In het startscript van de betreffende service vervangt u daarna java door dit volledige pad. De systeembrede standaard-Java blijft onaangeroerd en alle andere applicaties draaien gewoon door zoals voorheen.
Als het na de wissel toch niet start
De versiefout is weg, maar de server start nog altijd niet. Dat is normaal en heeft bijna altijd een van drie oorzaken.
Oude startflags. Wie van Java 8 naar 17 of 21 overstapt, sleept vaak startparameters mee die niet meer bestaan. De klassieker is de oude garbage collector, die in Java 14 is verwijderd:
Unrecognized VM option 'UseConcMarkSweepGC'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
Verwijder -XX:+UseConcMarkSweepGC en alle bijbehorende CMS-opties uit het startcommando. Moderne Java-versies gebruiken standaard G1, en voor Minecraft-servers vormen de actuele Aikar-flags de betere basis. De melding noemt de storende optie altijd bij naam, u hoeft dus niet te gokken.
Te weinig werkgeheugen. Verschijnt na de wissel Could not reserve enough space for object heap, dan is de waarde achter -Xmx groter dan het vrije geheugen. Nieuwere Java-versies houden zich strenger aan container- en cgroup-grenzen dan Java 8. Controleer het vrije geheugen en lees zo nodig ons artikel over swap en out of memory.
Geen Java meer in het alternatives-systeem. Wie de oude versie al te ijverig verwijdert, krijgt update-alternatives: error: no alternatives for java of simpelweg command not found. Dat herstelt u binnen een minuut door een willekeurig JRE-pakket opnieuw te installeren, en er gaat daarbij niets verloren. Voorzichtigheid is alleen geboden bij apt autoremove wanneer andere pakketten op default-jre voortbouwen: controleer de lijst met te verwijderen pakketten voordat u bevestigt.
Waaraan u ziet dat het gelukt is
Vertrouw niet op het uitblijven van de foutmelding, maar controleer drie punten na elkaar.
Ten eerste de versie in uw shell:
java -version 2>&1 | head -n 1
Ten tweede de versie waarmee het proces werkelijk draait. De hierboven getoonde blik op /proc is hier het eerlijkste gereedschap, want die vertrouwt noch omgevingsvariabelen noch symlinks. Wijst ls -l /proc/PID/exe naar de gewenste JVM-map, dan is de wissel echt doorgekomen.
Ten derde het logbestand van de applicatie. Bij een Minecraft-server is de regel Done (12.345s)! For help, type "help" het bewijs dat de start volledig is doorlopen. Bij een systemd-service controleert u dat met:
systemctl status minecraft
journalctl -u minecraft -n 50 --no-pager
Wilt u het helemaal precies weten, dan kunt u de JVM ook zover krijgen dat zij haar eigen configuratie afdrukt. Dat is handig wanneer er meerdere Java-installaties in het spel zijn en u eenduidig moet vaststellen om welke het gaat:
java -XshowSettings:properties -version
In de uitvoer vindt u onder meer java.home en java.version. Daarmee is de vraag welke installatie er op dit moment werkt definitief beantwoord.
Kort samengevat
Het getal in de fout is geen foutnummer, maar een versieaanduiding: major min 44 geeft de Java-versie, 52 is Java 8, 55 is Java 11, 61 is Java 17 en 65 is Java 21. De lange meldingstekst met has been compiled by a more recent version betekent dat uw Java te oud is, terwijl de korte tekst Unsupported class file major version zonder verdere context meestal wijst op een te nieuwe Java. Installeer de passende versie, houd er rekening mee dat Debian 12 alleen Java 17 en Debian 13 alleen Java 21 en 25 in de standaardpakketbronnen voert, en activeer de versie daarna ook echt, bij twijfel met een absoluut pad in de systemd-unit. Ter controle volstaat een blik op /proc/PID/exe, dan weet u zeker in plaats van bij benadering welke Java uw applicatie uitvoert.
Zet u een server helemaal opnieuw op en wilt u deze valkuilen van meet af aan vermijden, dan helpen onze artikelen over het inrichten van een nieuwe rootserver en over de installatie van een Minecraft-server op Debian bij een schone start.
Veelgestelde vragen
Wat betekent "class file version 65.0" precies?
Welke Java-versie heb ik nodig voor mijn Minecraft-server?
Ik heb Java 21 geïnstalleerd, maar java -version toont nog steeds Java 17. Waarom?
Waarom bestaat openjdk-21-jre-headless niet op Debian 12?
De fout luidt alleen "Unsupported class file major version 65" zonder verdere tekst. Wat nu?
Hoe kom ik erachter welke Java een al draaiend proces gebruikt?
Na de overstap naar Java 21 start de server met "Unrecognized VM option". Wat is er gebeurd?
2026 KernelHost GmbH. Alle rechten voorbehouden. Deze handleiding is auteursrechtelijk beschermd. Publicatie op andere websites, geheel, gedeeltelijk of in bewerkte vorm, is zonder onze schriftelijke toestemming niet toegestaan. Citeren met bronvermelding en link is uitdrukkelijk welkom.

