Naprawa błędu Javy „Unsupported class file major version”

Opublikowano 12 min czytania

Liczba w błędzie zdradza wszystko: 52 to Java 8, 55 to Java 11, 61 to Java 17, 65 to Java 21. Tak sprawdzisz, która Java naprawdę pracuje, i włączysz właściwą wersję.

Uruchamiasz serwer Minecraft, wtyczkę albo narzędzie do budowania, a zamiast spodziewanego startu w terminalu ląduje liczba, która na pierwszy rzut oka nic nie mówi: class file version 65.0. Dobra wiadomość jest taka, że ta liczba zawiera już całą diagnozę. Trzeba ją tylko umieć odczytać. Ten poradnik pokazuje, jak z tej liczby wyprowadzić potrzebną wersję Javy, jak ustalić, która Java naprawdę pracuje na twoim serwerze (często to nie ta, o której myślisz), oraz jak na stałe włączyć właściwą wersję.

Dwa różne komunikaty błędu, dwie różne przyczyny

Dokładne brzmienie ma tu decydujące znaczenie, bo za dwoma spotykanymi komunikatami kryją się dokładnie przeciwne problemy. Pierwszy pochodzi od samej maszyny wirtualnej Javy:

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

Ten komunikat oznacza zawsze to samo: aplikację zbudowano nowszą Javą, niż masz zainstalowaną. Pierwsza liczba mówi, co przynosi ze sobą aplikacja, druga mówi, co rozumie twoje środowisko uruchomieniowe. W powyższym przykładzie oprogramowanie żąda Javy 21, a zainstalowana jest Java 17.

Drugi wariant wygląda podobnie, ale bierze się zupełnie skądinąd:

java.lang.IllegalArgumentException: Unsupported class file major version 65

Tej krótkiej formy nie rzuca JVM, tylko biblioteka wczytująca i analizująca bajtkod, z reguły ASM. Pojawia się przy Gradle, przy starych ładowarkach wtyczek i przy starszych rdzeniach serwerowych. Tutaj sytuacja jest zwykle odwrotna: twoja Java jest za nowa dla oprogramowania, które chce odczytać bajtkod. Kto pomyli te dwa komunikaty, instaluje w złą stronę i dziwi się, że błąd zostaje. Zapamiętaj prostą zasadę: jeśli widnieje to długie zdanie z has been compiled by a more recent version, potrzebujesz nowszej Javy. Jeśli stoi tam wyłącznie krótkie zdanie, potrzebujesz prawdopodobnie starszej.

Rozszyfrowanie liczby: wersja główna minus 44

Przeliczenie jest prostsze, niż przedstawia to większość poradników. Wersja główna pliku class minus 44 daje wersję Javy. 65 minus 44 to 21 i koniec. Reguła obowiązuje bez wyjątku od Javy 1.1 i nie zmieni się też w przyszłości, bo każde nowe główne wydanie Javy podnosi tę liczbę dokładnie o jeden.

Class file versionWersja JavyTypowe miejsce występowania
52Java 8Minecraft do 1.16.5, stare modpacki Forge, starsze oprogramowanie korporacyjne
53Java 9rzadko spotykana, wersja przejściowa
55Java 11wiele bibliotek, starsze aplikacje Spring
60Java 16Minecraft 1.17.x
61Java 17Minecraft od 1.18 do 1.20.4, bardzo wiele aktualnych wtyczek
65Java 21Minecraft od 1.20.5, czyli cała seria 1.21, nowoczesne rdzenie serwerowe
69Java 25całkiem świeże kompilacje, aktualne wydanie LTS

Wartości pośrednie działają według tej samej reguły: 62 to Java 18, 63 to Java 19, 64 to Java 20, 66 to Java 22 i tak dalej. Człon .0 za liczbą to wersja poboczna i praktycznie nigdy nie ma znaczenia.

Co to konkretnie oznacza dla Minecrafta

Przyporządkowanie dla serwerów Minecraft jest jednoznaczne i da się je zapamiętać. Do 1.16.5 włącznie obowiązuje Java 8, dla 1.17.x co najmniej Java 16, od 1.18 do 1.20.4 co najmniej Java 17, a od 1.20.5 (czyli dla całej serii 1.21) Java 21. Jeśli więc w logu pojawia się class file version 65.0, a ty właśnie zaktualizowałeś serwer do nowszego wydania, przyczynę masz ustaloną, zanim w ogóle zajrzysz do konfiguracji.

Która Java naprawdę pracuje?

Najczęstszy błąd w rozumowaniu przy tej klasie problemów to założenie, że wynik polecenia java -version w twojej sesji SSH obowiązuje również dla działającej usługi. Często tak nie jest. Mimo to zacznij właśnie tutaj:

java -version

W pierwszym wierszu wyniku stoi wersja, na przykład openjdk version "21.0.11" 2026-04-15. Jeśli zamiast tego pojawia się bash: java: command not found, w ścieżce wyszukiwania nie ma w ogóle żadnej Javy, a aplikację uruchamia skrypt startowy podający ścieżkę bezwzględną. Sprawdź, jakie środowiska uruchomieniowe są zainstalowane:

ls /usr/lib/jvm
readlink -f "$(command -v java)"
update-alternatives --display java

readlink -f rozwija łańcuch symlinków i pokazuje faktycznie wykonywany plik binarny, na przykład /usr/lib/jvm/java-17-openjdk-amd64/bin/java. To ważniejsze niż sam numer wersji, bo tej ścieżki będziesz jeszcze potrzebować.

Środkowy wiersz świadomie korzysta z command -v, a nie z popularniejszego which. which to osobny program, którego minimalna instalacja AlmaLinux 9 i 10, Rocky Linux 9 oraz Oracle Linux 9 w ogóle nie zawiera. Wariant z which przewraca się tam dwukrotnie: najpierw which: command not found, potem readlink: missing operand. command -v siedzi w samym shellu i działa wszędzie.

Dla już działającego procesu istnieje droga, która nie zostawia miejsca na wątpliwości. Odpowiada ona na pytanie, którą Javą usługa faktycznie wystartowała, niezależnie od ścieżki wyszukiwania i zmiennych środowiskowych:

ls -l /proc/$(pgrep -f server.jar | head -n 1)/exe

Symlink exe wskazuje plik wykonywalny procesu. Jeśli stoi tam inna ścieżka niż oczekiwana, masz przyczynę: usługa startuje z inną Javą niż twój shell. Zdarza się to regularnie przy jednostkach systemd, bo przynoszą one własne, minimalne środowisko, a twoje JAVA_HOME z pliku .bashrc po prostu tam nie istnieje.

Odczyt wersji class wprost z pliku JAR

Czasem chcesz wiedzieć, jakiej Javy wymaga plik, zanim w ogóle go uruchomisz. Da się to zrobić bez instalowania dodatkowych narzędzi. Każdy plik .class zaczyna się sygnaturą CAFEBABE, po niej idą dwa bajty wersji pobocznej i dwa bajty wersji głównej. Ósmy bajt to więc szukana liczba:

unzip -p server.jar net/minecraft/bundler/Main.class | od -An -tu1 -N8

Wynik brzmi wtedy mniej więcej 202 254 186 190 0 0 0 65. Pierwsze cztery liczby to sygnatura, ostatnia to twoja odpowiedź: 65, czyli Java 21. Przy innych programach podmieniasz odpowiednio ścieżkę klasy, a właściwą nazwę wskaże unzip -l server.jar albo wpis Main-ClassMETA-INF/MANIFEST.MF. Jeśli masz zainstalowane JDK, jest jeszcze wygodniej:

javap -verbose -cp server.jar net.minecraft.bundler.Main | grep major

Przy wtyczkach ta sztuczka jest szczególnie użyteczna. Jeśli pojedyncza wtyczka przerywa start serwera, sprawdź jej klasę główną, a od razu będziesz wiedzieć, czy wtyczka jest za nowa dla twojego rdzenia serwerowego, czy odwrotnie.

Instalacja właściwej wersji Javy

Tutaj dystrybucje wyraźnie się rozjeżdżają i dokładnie w tym miejscu przewracają się poradniki pisane hurtem. Stan z prawdziwych systemów, sprawdzony w lipcu 2026:

PakietDebian 13Debian 12Ubuntu 24.04Ubuntu 22.04
openjdk-8-jre-headlessniedostępnyniedostępny8u4928u492
openjdk-11-jre-headlessniedostępnyniedostępny11.0.3111.0.31
openjdk-17-jre-headlessniedostępny17.0.1917.0.1917.0.19
openjdk-21-jre-headless21.0.11niedostępny21.0.1121.0.11
openjdk-25-jre-headless25.0.3niedostępny25.0.325.0.3

Przeczytaj tę tabelę raz na spokojnie, zaoszczędzi ci mnóstwo czasu. Debian 12 zna wyłącznie Javę 17, Debian 13 nie zna już Javy 17, tylko wersje 21 i 25. Kto więc chce prowadzić na Debianie 12 serwer Minecraft z serii 1.21 albo na Debianie 13 program z class file version 61.0, nie znajdzie w standardowych źródłach nic pasującego. Ubuntu jest w tym punkcie hojniejsze i dostarcza wszystko od Javy 8 do 25 z jednej ręki.

Przed instalacją sprawdź, czy pakiet w ogóle jest dostępny:

apt update
apt-cache policy openjdk-21-jre-headless

Jeśli przy Kandydująca, czyli Candidate na systemie anglojęzycznym, widnieje (brak), tego pakietu w twojej wersji nie ma. W przeciwnym razie zainstaluj go. Do samego uruchamiania wystarczy JRE, a pakiet -headless oszczędza zależności graficzne i na serwerze zawsze jest właściwym wyborem:

apt install -y openjdk-21-jre-headless

Kto kompiluje samodzielnie albo korzysta z Gradle bądź Mavena, potrzebuje JDK zamiast JRE, czyli openjdk-21-jdk-headless. Dokładniejsze drogi opisują nasze wpisy o Javie 17 na Debianie oraz Javie 21 na Debianie.

Gdy dystrybucja nie oferuje danej wersji: Adoptium Temurin

Na wszystkie przypadki, których standardowe źródła nie pokrywają, jest repozytorium Adoptium. Dostarcza ono Temurin od 8 do 26 dla Debiana 12, Debiana 13, Ubuntu 22.04 oraz Ubuntu 24.04 i jako jedyne rozwiązanie udostępnia na każdym z tych systemów dowolną potrzebną wersję. Ważne: dawniej zwyczajowa droga przez apt-key jest wycofana, klucz należy dziś umieścić w /etc/apt/keyrings/ i przypisać go przez signed-by dokładnie temu jednemu źródłu.

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

Wywołanie awk automatycznie wstawia właściwą nazwę kodową, czyli trixie, bookworm, noble albo jammy. Jeśli apt update przerywa potem z komunikatem Conflicting values set for option Signed-By, istnieje już starsze źródło Adoptium, zwykle założone przez extrepo. Poszukaj go w /etc/apt/sources.list.d/ i usuń zdublowany plik.

AlmaLinux, Rocky Linux i RHEL

W rodzinie Red Hat pakiety nazywają się inaczej i noszą wersję w nazwie, bez przedrostka openjdk na początku:

dnf install -y java-21-openjdk-headless

AlmaLinux 9, Rocky Linux 9 i Oracle Linux 9 prowadzą pełną serię od java-1.8.0-openjdk-headless po java-25-openjdk-headless. AlmaLinux 10 zrobił natomiast to samo cięcie co Debian 13 i zna już tylko wersje 21 oraz 25, więc dnf install java-17-openjdk-headless kończy się tam komunikatem No match for argument.

Także narzędzie do przełączania nazywa się tu po prostu alternatives, a update-alternatives to tylko symlink do niego. Zachowuje się jednak inaczej: alternatives --list nie przyjmuje żadnego argumentu. Znane z Debiana update-alternatives --list java wypisuje tam wyłącznie tekst pomocy i kończy się kodem powrotu 2. Używaj alternatives --list | grep java albo alternatives --display java, przy czym to drugie zgłasza java - status is auto. zamiast debianowej formy java - auto mode.

Ścieżki instalacyjne noszą w rodzinie Red Hat pełną wersję pakietu w nazwie katalogu, czyli na przykład /usr/lib/jvm/java-21-openjdk-21.0.11.0.10-1.el9.x86_64, a nie krótką formę debianową z przyrostkiem architektury. Tylko AlmaLinux 10 zakłada dodatkowo krótki symlink java-21-openjdk. Nie przepisuj więc ścieżki z głowy, tylko wyciągnij ją z alternatives --display java.

Zachowanie, które bywa tu szczególnie podstępne: po instalacji Javy 17 obok obecnej już Javy 21 mechanizm alternatives na AlmaLinux 9 samoczynnie przestawił link /usr/bin/java na starszą wersję 17. Kto więc dokłada drugą wersję tylko po to, żeby użyć jej celowo do starej aplikacji, zmienia przy okazji domyślną Javę całego systemu. Po każdej dodatkowej instalacji ustaw wyraźnie alternatives --set java <ścieżka> i skontroluj wynik poleceniem java -version.

Włączenie właściwej wersji

Zainstalowana nie znaczy aktywna. Po instalacji drugiego JDK java -version często dalej pokazuje starą wersję, bo symlink /usr/bin/java pozostaje bez zmian. Na Debianie i Ubuntu zajmuje się tym mechanizm alternatives:

update-alternatives --list java
update-alternatives --config java

Drugie polecenie pokazuje numerowaną listę i pyta o twój wybór. Potem wybór stoi na manual i nie nadpisują go już przyszłe instalacje pakietów, a dokładnie o to chodzi. Kto chce to oskryptować bez pytania zwrotnego, korzysta z wariantu set z pełną ścieżką:

update-alternatives --set java /usr/lib/jvm/temurin-21-jdk-amd64/bin/java

Ustaw dodatkowo JAVA_HOME, jeśli w grze są narzędzia do budowania. Gradle i Maven ignorują symlink i kierują się właśnie tą zmienną:

export JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64
$JAVA_HOME/bin/java -version

Zwróć uwagę, że export obowiązuje wyłącznie w bieżącej sesji. Usługom nie pomaga w ogóle: po najbliższym restarcie usługa znów bierze starą wersję Javy, bo nigdy tej zmiennej nie zobaczyła. Na stałe JAVA_HOME należy więc wpisać do jednostki systemd jako Environment= albo, dla całego systemu, do /etc/environment.

Najważniejszy krok przy usługach

Jeśli twoja aplikacja działa jako usługa systemd, symlink alternatives to dopiero połowa sukcesu. Usługa startuje z własnym środowiskiem, a kiedy w ExecStart stoi stara ścieżka bezwzględna, żadne update-alternatives tego świata nic nie zmieni. Wpisz pełną ścieżkę, żeby wersja była ustalona niezależnie od stanu systemu:

[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

Potem koniecznie przeładuj konfigurację, inaczej dalej działa stara definicja:

systemctl daemon-reload
systemctl restart minecraft

Jak taka jednostka wygląda w całości, pokazują nasze wpisy Tworzenie usługi systemd oraz Automatyczny start serwera Minecraft. Ta sama pułapka dotyczy skryptów startowych działających przez screen albo tmux, a także paneli w rodzaju Pterodactyl, które ustalają wersję Javy w obrazie kontenera, a nie na systemie gospodarza.

Przypadek odwrotny: Java jest za nowa

Znacznie rzadszy, za to bardziej mylący, jest przypadek odwrotny. Uruchamiasz starszy modpack albo stare narzędzie do budowania na świeżo postawionym serwerze z Javą 21 i dostajesz krótkie zdanie Unsupported class file major version 65, choć przecież wszystko jest aktualne. Tutaj biblioteka zgłasza, że nie umie nic zrobić z formatem bajtkodu twojego nowego środowiska uruchomieniowego. Typowe źródła to modpacki Forge dla 1.12.2, starsze wersje Gradle oraz mocno już leciwe ładowarki wtyczek.

Rozwiązaniem nie jest usunięcie nowej Javy. Zainstaluj zamiast tego starą wersję równolegle i odwołuj się do niej celowo ścieżką bezwzględną. Dokładnie po to zbudowano mechanizm alternatives i dokładnie dlatego opłaca się trzymać na jednym systemie Javę 8 oraz Javę 21 naraz. Na Ubuntu da się to zrobić wprost ze standardowych źródeł, na Debianie przez Temurin:

apt install -y temurin-8-jdk
/usr/lib/jvm/temurin-8-jdk-amd64/bin/java -version

W skrypcie startowym danej usługi zastępujesz potem java tą pełną ścieżką. Domyślna Java całego systemu zostaje nietknięta, a wszystkie pozostałe aplikacje działają dalej jak dotąd.

Gdy po zmianie i tak nie startuje

Błąd wersji zniknął, a serwer nadal nie startuje. To normalne i prawie zawsze ma jedną z trzech przyczyn.

Stare flagi startowe. Kto przechodzi z Javy 8 na 17 albo 21, często ciągnie za sobą parametry startowe, których już nie ma. Klasykiem jest stary garbage collector, usunięty w Javie 14:

Unrecognized VM option 'UseConcMarkSweepGC'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

Usuń -XX:+UseConcMarkSweepGC i wszystkie powiązane opcje CMS z polecenia startowego. Nowoczesne wersje Javy korzystają domyślnie z G1, a dla serwerów Minecraft lepszą podstawą są aktualne flagi Aikara. Komunikat zawsze wymienia problematyczną opcję z nazwy, nie musisz więc zgadywać.

Za mało pamięci RAM. Jeśli po zmianie pojawia się Could not reserve enough space for object heap, wartość za -Xmx jest większa niż wolna pamięć. Nowsze wersje Javy respektują limity kontenerów i cgroup surowiej niż Java 8. Sprawdź wolną pamięć i w razie potrzeby przeczytaj nasz wpis o swapie i błędach Out-of-Memory.

W systemie alternatives nie ma już żadnej Javy. Kto z nadmiernym zapałem odinstaluje starą wersję, dostanie update-alternatives: error: no alternatives for java albo po prostu command not found. Naprawia się to w minutę, instalując ponownie dowolny pakiet JRE, i nic przy tym nie ginie. Ostrożność jest wskazana tylko przy apt autoremove, gdy inne pakiety opierają się na default-jre: sprawdź listę pakietów do usunięcia, zanim potwierdzisz.

Po czym poznasz, że się udało

Nie polegaj na tym, że komunikat błędu zniknął, tylko sprawdź po kolei trzy punkty.

Po pierwsze, wersję w twoim shellu:

java -version 2>&1 | head -n 1

Po drugie, wersję, z którą proces faktycznie działa. Pokazany wyżej rzut oka na /proc jest tutaj najuczciwszym narzędziem, bo nie ufa ani zmiennym środowiskowym, ani symlinkom. Jeśli ls -l /proc/PID/exe wskazuje na pożądany katalog JVM, zmiana naprawdę doszła do skutku.

Po trzecie, log aplikacji. Przy serwerze Minecraft dowodem na to, że start przebiegł w całości, jest wiersz Done (12.345s)! For help, type "help". Przy usłudze systemd sprawdzisz to tak:

systemctl status minecraft
journalctl -u minecraft -n 50 --no-pager

Jeśli chcesz wiedzieć całkiem dokładnie, JVM da się też skłonić do wypisania własnej konfiguracji. Przydaje się to, gdy w grze jest kilka instalacji Javy, a ty musisz jednoznacznie zidentyfikować jedną z nich:

java -XshowSettings:properties -version

W wyniku znajdziesz między innymi java.home oraz java.version. Tym samym pytanie o to, która instalacja właśnie pracuje, jest ostatecznie rozstrzygnięte.

W skrócie

Liczba w błędzie to nie numer błędu, tylko informacja o wersji: wersja główna minus 44 daje wersję Javy, 52 to Java 8, 55 to Java 11, 61 to Java 17, a 65 to Java 21. Długi komunikat z has been compiled by a more recent version znaczy, że twoja Java jest za stara, a krótki tekst Unsupported class file major version bez dalszego kontekstu wskazuje zwykle na Javę zbyt nową. Zainstaluj pasującą wersję, pamiętaj, że Debian 12 prowadzi w standardowych źródłach tylko Javę 17, a Debian 13 wyłącznie wersje 21 i 25, a potem naprawdę tę wersję włącz, w razie wątpliwości pełną ścieżką w jednostce systemd. Do kontroli wystarczy rzut oka na /proc/PID/exe, wtedy wiesz na pewno, a nie mniej więcej, która Java wykonuje twoją aplikację.

Jeśli stawiasz serwer od nowa i chcesz uniknąć tych pułapek od samego początku, w czystym starcie pomogą nasze wpisy o konfiguracji nowego serwera root oraz o instalacji serwera Minecraft na Debianie.

Najczęstsze pytania

Co dokładnie oznacza „class file version 65.0”?
Aplikację skompilowano Javą 21. Reguła brzmi: wersja główna minus 44, czyli 65 minus 44 daje 21. Druga liczba w komunikacie podaje najwyższą wersję, jaką rozumie twoje zainstalowane środowisko uruchomieniowe. Jeśli stoi tam 61, pracuje u ciebie Java 17.
Jakiej wersji Javy potrzebuję do swojego serwera Minecraft?
Do 1.16.5 włącznie Java 8, dla 1.17.x Java 16, od 1.18 do 1.20.4 Java 17, a od 1.20.5 oraz dla całej serii 1.21 Java 21. Odpowiada to wersjom pliku class 52, 60, 61 i 65.
Zainstalowałem Javę 21, ale java -version dalej pokazuje Javę 17. Dlaczego?
Instalacja nie zmienia automatycznie symlinku /usr/bin/java. Przełącz go poleceniem update-alternatives --config java. Jeśli aplikacja działa jako usługa, sprawdź dodatkowo ścieżkę w ExecStart jednostki systemd, bo tam wersja bywa wpisana na sztywno.
Dlaczego na Debianie 12 nie ma pakietu openjdk-21-jre-headless?
Debian 12 prowadzi w standardowych źródłach wyłącznie Javę 17, a Debian 13 tylko wersje 21 i 25. Jeśli potrzebujesz wersji, której twoja dystrybucja nie oferuje, sięgnij po repozytorium Adoptium: udostępnia ono Temurin od 8 do 26 dla wszystkich czterech popularnych wydań Debiana i Ubuntu.
Błąd brzmi tylko „Unsupported class file major version 65”, bez dalszego tekstu. Co robić?
Ta krótka forma pochodzi zwykle nie od JVM, tylko od biblioteki wczytującej bajtkod, na przykład ASM. W takim przypadku twoja Java jest za nowa dla tego oprogramowania. Zainstaluj równolegle pasującą starszą wersję i uruchamiaj daną aplikację celowo jej ścieżką bezwzględną.
Jak sprawdzić, której Javy używa już działający proces?
Przez symlink exe w systemie plików proc: polecenie ls -l /proc/$(pgrep -f server.jar | head -n 1)/exe pokazuje faktycznie wykonywany plik binarny, niezależnie od ścieżki wyszukiwania, JAVA_HOME i konfiguracji alternatives.
Po przejściu na Javę 21 serwer kończy start komunikatem „Unrecognized VM option”. Co się stało?
Twoje polecenie startowe zawiera parametry, których w nowszych wersjach Javy już nie ma, typowo -XX:+UseConcMarkSweepGC. Ten garbage collector usunięto w Javie 14. Usuń wskazaną opcję z polecenia startowego, komunikat błędu zawsze wymienia ją z nazwy.

Java Minecraft Debian Ubuntu Rozwiązywanie problemów Serwery gier OpenJDK Temurin