Как исправить ошибку Java «Unsupported class file major version»

Опубликовано 13 мин. чтения

Число в ошибке говорит всё: 52 — это Java 8, 55 — Java 11, 61 — Java 17, 65 — Java 21. Показываем, как выяснить, какая Java работает на самом деле, и как активировать нужную версию.

Вы запускаете сервер Minecraft, плагин или инструмент сборки, и вместо ожидаемого старта в терминале появляется число, которое на первый взгляд ни о чём не говорит: class file version 65.0. Хорошая новость в том, что это число уже содержит полный диагноз. Нужно лишь уметь его прочитать. В этой статье показано, как по числу определить нужную версию Java, как выяснить, какая Java на самом деле работает на вашем сервере (часто это не та, о которой вы думаете), и как включить правильную версию на постоянной основе.

Два разных сообщения об ошибке, две разные причины

Формулировка здесь решает всё, потому что за двумя распространёнными сообщениями скрываются прямо противоположные проблемы. Первое приходит от самой виртуальной машины Java:

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

Это сообщение всегда означает одно и то же: приложение собрано более новой Java, чем установлена у вас. Первое число — то, что приносит с собой приложение, второе число — то, что понимает ваша среда выполнения. В примере выше программа требует Java 21, а установлена Java 17.

Второй вариант выглядит похоже, но приходит совсем с другой стороны:

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

Эту короткую форму выбрасывает не JVM, а библиотека, которая читает и анализирует байт-код, как правило ASM. Она встречается в Gradle, в старых загрузчиках плагинов и в устаревших серверных ядрах. Здесь ситуация обычно обратная: ваша Java слишком новая для программы, которая пытается прочитать байт-код. Тот, кто путает эти два сообщения, устанавливает версию не в ту сторону и удивляется, что ошибка остаётся. Запомните простое правило: если стоит длинная фраза с has been compiled by a more recent version, вам нужна более новая Java. Если стоит только короткая фраза, вам, скорее всего, нужна более старая.

Расшифровка числа: мажорная версия минус 44

Пересчёт проще, чем его подают в большинстве руководств. Мажорная версия class-файла минус 44 даёт версию Java. 65 минус 44 равно 21, и на этом всё. Правило действует без исключений начиная с Java 1.1 и не изменится и в будущем, потому что каждая новая основная версия Java увеличивает число ровно на единицу.

Class file versionВерсия JavaГде встречается чаще всего
52Java 8Minecraft до 1.16.5, старые сборки модов Forge, устаревшее корпоративное ПО
53Java 9редко, переходная версия
55Java 11многие библиотеки, старые приложения на Spring
60Java 16Minecraft 1.17.x
61Java 17Minecraft с 1.18 по 1.20.4, очень многие актуальные плагины
65Java 21Minecraft начиная с 1.20.5, то есть вся линейка 1.21, современные серверные ядра
69Java 25самые свежие сборки, актуальный LTS-релиз

Промежуточные значения работают по тому же правилу: 62 — это Java 18, 63 — Java 19, 64 — Java 20, 66 — Java 22 и так далее. .0 после числа — это минорная версия, и на практике она не играет почти никакой роли.

Что это означает для Minecraft на практике

Соответствие для серверов Minecraft однозначное, его вполне можно выучить наизусть. До версии 1.16.5 включительно нужна Java 8, для 1.17.x минимум Java 16, с 1.18 по 1.20.4 минимум Java 17, начиная с 1.20.5 (а значит, и для всей линейки 1.21) Java 21. Поэтому если в логе появляется class file version 65.0, а вы только что обновили сервер до актуальной версии, причина найдена ещё до того, как вы заглянули в конфигурацию.

Какая Java работает на самом деле?

Самая частая логическая ошибка при таких сбоях — считать, что вывод java -version в вашей SSH-сессии относится и к работающей службе. Часто это не так. Начать всё равно стоит отсюда:

java -version

В первой строке вывода указана версия, например openjdk version "21.0.11" 2026-04-15. Если вместо этого появляется bash: java: command not found, то Java нет в путях поиска вообще, а приложение стартует из скрипта с абсолютным путём. Посмотрите, какие среды выполнения установлены:

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

readlink -f разворачивает цепочку символьных ссылок и показывает реально исполняемый файл, например /usr/lib/jvm/java-17-openjdk-amd64/bin/java. Это важнее, чем один только номер версии, потому что этот путь понадобится вам дальше.

Средняя строка намеренно написана через command -v, а не через более привычный which. which — это отдельная программа, и в минимальной установке AlmaLinux 9 и 10, Rocky Linux 9 и Oracle Linux 9 её попросту нет. Там вариант с which проваливается дважды: сначала which: command not found, затем readlink: missing operand. command -v встроен в саму оболочку и работает везде.

Для уже запущенного процесса есть способ, не оставляющий никаких сомнений. Он отвечает на вопрос, с какой именно Java была запущена служба, независимо от путей поиска и переменных окружения:

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

Символьная ссылка exe указывает на исполняемый файл процесса. Если там стоит не тот путь, который вы ожидали, причина найдена: служба стартует не с той Java, что ваша оболочка. Регулярно это случается с юнитами systemd, потому что у них своё минимальное окружение, и вашей переменной JAVA_HOME из .bashrc там просто не существует.

Читаем версию class-файла прямо из JAR-архива

Иногда нужно узнать, какую Java требует файл, ещё до его запуска. Это возможно без установки дополнительных инструментов. Каждый файл .class начинается с сигнатуры CAFEBABE, за ней идут два байта минорной и два байта мажорной версии. То есть восьмой байт и есть искомое число:

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

Вывод будет примерно таким: 202 254 186 190 0 0 0 65. Первые четыре числа — это сигнатура, последнее — ваш ответ: 65, то есть Java 21. Для других программ подставьте соответствующий путь к классу, нужное имя подскажет unzip -l server.jar или запись Main-Class в META-INF/MANIFEST.MF. Если установлен JDK, есть способ удобнее:

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

Для плагинов этот приём особенно полезен. Если старт сервера прерывает какой-то один плагин, проверьте его главный класс и сразу узнаете, слишком ли он новый для вашего серверного ядра или наоборот.

Установка подходящей версии Java

Здесь дистрибутивы заметно расходятся, и универсальные инструкции перестают работать именно в этом месте. Состояние на реальных системах, проверено в июле 2026 года:

ПакетDebian 13Debian 12Ubuntu 24.04Ubuntu 22.04
openjdk-8-jre-headlessотсутствуетотсутствует8u4928u492
openjdk-11-jre-headlessотсутствуетотсутствует11.0.3111.0.31
openjdk-17-jre-headlessотсутствует17.0.1917.0.1917.0.19
openjdk-21-jre-headless21.0.11отсутствует21.0.1121.0.11
openjdk-25-jre-headless25.0.3отсутствует25.0.325.0.3

Прочитайте эту таблицу один раз внимательно, она сэкономит вам много времени. Debian 12 знает только Java 17, Debian 13 Java 17 уже не знает, а предлагает лишь 21 и 25. Поэтому тот, кто хочет запустить на Debian 12 сервер Minecraft версии 1.21 или на Debian 13 программу с class file version 61.0, ничего подходящего в стандартных репозиториях не найдёт. Ubuntu в этом отношении щедрее и отдаёт всё от Java 8 до 25 из одного источника.

Перед установкой проверьте, доступен ли пакет вообще:

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

Если в строке Кандидат (на англоязычной системе Candidate) стоит (нет), значит, в вашей версии дистрибутива такого пакета нет. В противном случае устанавливайте его. Для простого запуска достаточно JRE, а пакет -headless экономит графические зависимости и на сервере всегда является правильным выбором:

apt install -y openjdk-21-jre-headless

Тем, кто компилирует сам или использует Gradle либо Maven, нужен JDK вместо JRE, то есть openjdk-21-jdk-headless. Более подробные варианты описаны в наших статьях про Java 17 на Debian и Java 21 на Debian.

Если дистрибутив не предлагает нужную версию: Adoptium Temurin

Для всех случаев, которые стандартные репозитории не покрывают, есть репозиторий Adoptium. Он поставляет Temurin с 8 по 26 для Debian 12, Debian 13, Ubuntu 22.04 и Ubuntu 24.04 и остаётся тем самым единственным решением, дающим любую нужную версию на каждой из этих систем. Важно: прежний способ через apt-key объявлен устаревшим, ключ сегодня кладут в /etc/apt/keyrings/ и через signed-by привязывают ровно к этому одному репозиторию.

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

Вызов awk автоматически подставляет правильное кодовое имя, то есть trixie, bookworm, noble или jammy. Если после этого apt update прерывается сообщением Conflicting values set for option Signed-By, значит, старый репозиторий Adoptium уже существует, чаще всего заведённый через extrepo. Найдите его в /etc/apt/sources.list.d/ и удалите дублирующий файл.

AlmaLinux, Rocky Linux и RHEL

В семействе Red Hat пакеты называются иначе и несут версию в имени без префикса openjdk в начале:

dnf install -y java-21-openjdk-headless

AlmaLinux 9, Rocky Linux 9 и Oracle Linux 9 содержат полный ряд от java-1.8.0-openjdk-headless до java-25-openjdk-headless. AlmaLinux 10, напротив, провёл тот же обрез, что и Debian 13, и знает только 21 и 25: команда dnf install java-17-openjdk-headless завершается там сообщением No match for argument.

Инструмент переключения здесь тоже называется просто alternatives, а update-alternatives — лишь символьная ссылка на него. Ведёт себя он, однако, иначе: alternatives --list не принимает аргумент. Привычная по Debian команда update-alternatives --list java выводит там только справку и завершается с кодом возврата 2. Используйте alternatives --list | grep java или alternatives --display java, причём последняя сообщает java - status is auto. вместо привычной для Debian формы java - auto mode.

Пути установки в семействе Red Hat содержат в имени каталога полную версию пакета, например /usr/lib/jvm/java-21-openjdk-21.0.11.0.10-1.el9.x86_64, а не короткую форму Debian с суффиксом архитектуры. Только AlmaLinux 10 дополнительно создаёт короткую символьную ссылку java-21-openjdk. Поэтому не переписывайте путь по памяти, а берите его из вывода alternatives --display java.

Особенно коварно ведёт себя вот что: после установки Java 17 рядом с уже имевшейся Java 21 механизм alternatives на AlmaLinux 9 самостоятельно перевёл ссылку /usr/bin/java на более старую версию 17. То есть тот, кто доустанавливает вторую версию только ради одного старого приложения, попутно и незаметно меняет системную Java по умолчанию. После каждой такой доустановки явно выполняйте alternatives --set java <путь> и проверяйте результат через java -version.

Активируем нужную версию

Установлено не значит активно. После установки второго JDK java -version часто продолжает показывать старую версию, потому что символьная ссылка /usr/bin/java остаётся прежней. В Debian и Ubuntu этим управляет механизм alternatives:

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

Вторая команда показывает нумерованный список и спрашивает, что выбрать. После этого выбор переходит в режим manual и больше не перезаписывается будущими установками пакетов, а именно это и требуется. Кто хочет сделать то же самое в скрипте, без вопросов, использует вариант с set и полным путём:

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

Дополнительно задайте JAVA_HOME, если в деле участвуют инструменты сборки. Gradle и Maven игнорируют символьную ссылку и ориентируются на эту переменную:

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

Учтите, что export действует только в текущей сессии. Службам он не помогает совсем: после следующей перезагрузки служба снова возьмёт старую версию Java, потому что переменной она никогда не видела. Поэтому на постоянной основе JAVA_HOME прописывают в юнит systemd как Environment= или, для всей системы, в /etc/environment.

Самый важный шаг для служб

Если ваше приложение работает как служба systemd, символьная ссылка alternatives решает лишь половину задачи. Служба стартует со своим окружением, и если в ExecStart прописан старый абсолютный путь, никакой update-alternatives на свете этого не изменит. Впишите полный путь, чтобы версия была зафиксирована независимо от состояния системы:

[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

После этого обязательно перечитайте конфигурацию, иначе продолжит работать старое определение:

systemctl daemon-reload
systemctl restart minecraft

Как такой юнит выглядит целиком, показывают наши статьи создание службы systemd и автоматический запуск сервера Minecraft. Та же ловушка касается стартовых скриптов, работающих через screen или tmux, а также панелей вроде Pterodactyl, которые задают версию Java в образе контейнера, а не в хост-системе.

Обратный случай: Java слишком новая

Заметно реже, зато куда запутаннее выглядит противоположная ситуация. Вы запускаете старую сборку модов или старый инструмент сборки на свежеустановленном сервере с Java 21 и получаете короткую фразу Unsupported class file major version 65, хотя всё вроде бы актуальное. Здесь библиотека сообщает, что не понимает формат байт-кода вашей новой среды выполнения. Типичные виновники: сборки Forge для 1.12.2, старые версии Gradle и порядком устаревшие загрузчики плагинов.

Решение состоит не в том, чтобы удалить новую Java. Вместо этого установите старую версию параллельно и обращайтесь к ней целенаправленно по абсолютному пути. Ровно для этого механизм alternatives и создан, и ровно поэтому имеет смысл держать Java 8 и Java 21 на одной системе одновременно. В Ubuntu это делается прямо из стандартных репозиториев, в Debian через Temurin:

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

В стартовом скрипте затронутой службы замените затем java на этот полный путь. Системная Java по умолчанию остаётся нетронутой, все остальные приложения продолжают работать как прежде.

Если после смены версии всё равно не запускается

Ошибка версии ушла, но сервер по-прежнему не стартует. Это нормально и почти всегда имеет одну из трёх причин.

Старые флаги запуска. При переходе с Java 8 на 17 или 21 часто тянутся параметры запуска, которых больше не существует. Классика жанра — старый сборщик мусора, удалённый в Java 14:

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

Уберите -XX:+UseConcMarkSweepGC и все связанные опции CMS из команды запуска. Современные версии Java используют G1 по умолчанию, а для серверов Minecraft лучшей основой будут актуальные флаги Aikar. Сообщение всегда называет мешающую опцию по имени, поэтому гадать не придётся.

Недостаточно оперативной памяти. Если после смены версии появляется Could not reserve enough space for object heap, значит, значение после -Xmx больше объёма свободной памяти. Более новые версии Java строже учитывают ограничения контейнеров и cgroup, чем Java 8. Проверьте свободную память и при необходимости прочитайте нашу статью про swap и out-of-memory.

В системе alternatives не осталось Java. Кто слишком усердно удалил старую версию, получает update-alternatives: error: no alternatives for java или просто command not found. Это чинится за минуту переустановкой любого пакета JRE, ничего при этом не теряется. Осторожность нужна только с apt autoremove, если другие пакеты опираются на default-jre: проверьте список удаляемых пакетов, прежде чем подтверждать.

Как понять, что всё получилось

Не полагайтесь на отсутствие сообщения об ошибке, а проверьте три пункта по порядку.

Во-первых, версию в вашей оболочке:

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

Во-вторых, версию, с которой процесс действительно работает. Показанный выше взгляд в /proc здесь самый честный инструмент, потому что он не доверяет ни переменным окружения, ни символьным ссылкам. Если ls -l /proc/PID/exe указывает на нужный каталог JVM, смена версии действительно состоялась.

В-третьих, лог приложения. У сервера Minecraft строка Done (12.345s)! For help, type "help" служит доказательством того, что запуск прошёл полностью. У службы systemd это проверяется так:

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

Если хочется знать совсем точно, JVM можно заставить вывести собственную конфигурацию. Это удобно, когда в деле несколько установок Java и одну из них нужно однозначно опознать:

java -XshowSettings:properties -version

В выводе вы найдёте, среди прочего, java.home и java.version. После этого вопрос, какая установка сейчас работает, закрыт окончательно.

Коротко о главном

Число в сообщении об ошибке — это не код ошибки, а указание версии: мажорная минус 44 даёт версию Java, 52 — это Java 8, 55 — Java 11, 61 — Java 17, 65 — Java 21. Длинный текст с has been compiled by a more recent version означает, что ваша Java слишком старая, а короткий текст Unsupported class file major version без дополнительного контекста обычно указывает на слишком новую Java. Установите подходящую версию, помните, что в стандартных репозиториях Debian 12 есть только Java 17, а в Debian 13 только Java 21 и 25, и после этого действительно активируйте нужную версию, в спорном случае через абсолютный путь в юните systemd. Для контроля достаточно взгляда на /proc/PID/exe, тогда вы будете знать точно, а не приблизительно, какая Java выполняет ваше приложение.

Если вы настраиваете сервер с нуля и хотите обойти эти подводные камни заранее, чистому старту помогут наши статьи о настройке нового root-сервера и об установке сервера Minecraft на Debian.

Частые вопросы

Что именно означает «class file version 65.0»?
Приложение скомпилировано в Java 21. Правило простое: мажорная версия минус 44, то есть 65 минус 44 даёт 21. Второе число в сообщении называет максимальную версию, которую понимает установленная у вас среда выполнения. Если там стоит 61, у вас работает Java 17.
Какая версия Java нужна моему серверу Minecraft?
До 1.16.5 включительно Java 8, для 1.17.x Java 16, с 1.18 по 1.20.4 Java 17, а начиная с 1.20.5 и для всей линейки 1.21 Java 21. Этому соответствуют версии class-файлов 52, 60, 61 и 65.
Я установил Java 21, но java -version по-прежнему показывает Java 17. Почему?
Установка не меняет символьную ссылку /usr/bin/java автоматически. Переключитесь командой update-alternatives --config java. Если приложение работает как служба, проверьте дополнительно путь в ExecStart юнита systemd: там версия часто прописана жёстко.
Почему в Debian 12 нет пакета openjdk-21-jre-headless?
В стандартных репозиториях Debian 12 есть только Java 17, а в Debian 13, наоборот, только Java 21 и 25. Если нужна версия, которую ваш дистрибутив не предлагает, используйте репозиторий Adoptium: он даёт Temurin с 8 по 26 для всех четырёх распространённых версий Debian и Ubuntu.
Ошибка состоит только из строки «Unsupported class file major version 65», без другого текста. Что делать?
Эта короткая форма приходит обычно не от JVM, а от библиотеки вроде ASM, которая читает байт-код. В таком случае ваша Java слишком новая для программы. Установите подходящую более старую версию параллельно и запускайте затронутое приложение целенаправленно по её абсолютному пути.
Как узнать, какую Java использует уже запущенный процесс?
Через символьную ссылку exe в файловой системе proc: команда ls -l /proc/$(pgrep -f server.jar | head -n 1)/exe показывает реально исполняемый файл, независимо от путей поиска, JAVA_HOME и настроек alternatives.
После перехода на Java 21 сервер стартует с «Unrecognized VM option». Что случилось?
В вашей команде запуска есть параметры, которых в новых версиях Java больше нет, чаще всего -XX:+UseConcMarkSweepGC. Этот сборщик мусора удалён в Java 14. Уберите названную опцию из команды запуска: сообщение об ошибке всегда называет её по имени.

Java Minecraft Debian Ubuntu Устранение ошибок Игровой сервер OpenJDK Temurin