Resolver o erro Java "Unsupported class file major version"

Publicado a 16 min de leitura

O número no erro diz tudo: 52 é Java 8, 55 é Java 11, 61 é Java 17 e 65 é Java 21. Veja como descobrir que Java corre realmente e como ativar a versão certa.

Arranca um servidor Minecraft, um plugin ou uma ferramenta de build e, em vez do arranque esperado, o terminal mostra um número que à primeira vista não diz nada: class file version 65.0. A boa notícia é que esse número já contém todo o diagnóstico. Só precisa de o saber ler. Este artigo mostra como deduzir a versão de Java necessária a partir do número, como descobrir que Java corre realmente no seu servidor (muitas vezes não é aquele que pensa) e como ativar a versão certa de forma permanente.

Duas mensagens de erro diferentes, duas causas diferentes

A formulação exata é decisiva, porque por trás das duas mensagens habituais escondem-se problemas exatamente opostos. A primeira vem da própria Java Virtual Machine:

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

Esta mensagem significa sempre o mesmo: a aplicação foi compilada com um Java mais recente do que aquele que tem instalado. O primeiro número é o que a aplicação traz consigo, o segundo é o que o seu ambiente de execução compreende. No exemplo acima, o software exige o Java 21 e o que está instalado é o Java 17.

A segunda variante parece semelhante, mas vem de outro lado:

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

Esta forma curta não é lançada pela JVM, mas por uma biblioteca que lê e analisa bytecode, quase sempre o ASM. Aparece no Gradle, em carregadores de plugins antigos e em núcleos de servidor mais antigos. Aqui a situação costuma ser inversa: o seu Java é demasiado recente para o software que quer ler o bytecode. Quem confunde estas duas mensagens instala na direção errada e depois estranha que o erro se mantenha. Guarde esta regra prática: se aparecer a frase longa com has been compiled by a more recent version, precisa de um Java mais recente. Se aparecer apenas a frase curta, provavelmente precisa de um mais antigo.

Descodificar o número: versão major menos 44

A conversão é mais simples do que a maioria dos guias faz parecer. A versão major do ficheiro class menos 44 dá a versão do Java. 65 menos 44 é 21, e está feito. A regra vale sem falhas desde o Java 1.1 e também não vai mudar no futuro, porque cada nova versão principal do Java aumenta o número em exatamente uma unidade.

Class file versionVersão do JavaOnde aparece normalmente
52Java 8Minecraft até 1.16.5, modpacks Forge antigos, software empresarial mais antigo
53Java 9rara, versão de transição
55Java 11muitas bibliotecas, aplicações Spring mais antigas
60Java 16Minecraft 1.17.x
61Java 17Minecraft 1.18 até 1.20.4, muitos plugins atuais
65Java 21Minecraft a partir de 1.20.5, ou seja, toda a série 1.21, núcleos de servidor modernos
69Java 25builds acabadas de sair, versão LTS atual

Os valores intermédios seguem a mesma regra: 62 é Java 18, 63 é Java 19, 64 é Java 20, 66 é Java 22 e assim por diante. O .0 a seguir ao número é a versão minor e, na prática, nunca tem importância.

O que isto significa em concreto para o Minecraft

A correspondência para servidores Minecraft é clara e vale a pena decorá-la. Até à 1.16.5, inclusive, aplica-se o Java 8; para a 1.17.x, no mínimo o Java 16; da 1.18 até à 1.20.4, no mínimo o Java 17; a partir da 1.20.5 (e portanto para toda a série 1.21), o Java 21. Ou seja, se no log surgir class file version 65.0 e tiver acabado de atualizar para uma versão recente, a causa está encontrada antes sequer de olhar para a configuração.

Que Java corre realmente?

O erro de raciocínio mais frequente nesta classe de problemas é assumir que o resultado de java -version na sua sessão SSH também vale para o serviço em execução. Muitas vezes não vale. Ainda assim, comece por aqui:

java -version

A primeira linha do resultado indica a versão, por exemplo openjdk version "21.0.11" 2026-04-15. Se em vez disso aparecer bash: java: command not found, não existe qualquer Java no caminho de pesquisa e a aplicação é arrancada por um script com caminho absoluto. Verifique que ambientes de execução estão instalados:

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

O readlink -f resolve a cadeia de symlinks e mostra-lhe o binário que é realmente executado, por exemplo /usr/lib/jvm/java-17-openjdk-amd64/bin/java. Isso é mais importante do que o número da versão isolado, porque vai precisar desse caminho mais à frente.

A linha do meio está escrita de propósito com command -v e não com o mais divulgado which. O which é um programa próprio e, na instalação mínima do AlmaLinux 9 e 10, do Rocky Linux 9 e do Oracle Linux 9, não vem incluído. Aí a variante com which falha duas vezes: primeiro which: command not found, depois readlink: missing operand. O command -v está na própria shell e funciona em todo o lado.

Para um processo já em execução existe um caminho que não deixa margem para dúvidas. Responde à pergunta de com que Java o serviço foi mesmo arrancado, independentemente do caminho de pesquisa e das variáveis de ambiente:

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

O symlink exe aponta para o ficheiro executável do processo. Se aí estiver um caminho diferente do esperado, encontrou a causa: o serviço arranca com um Java diferente do da sua shell. Isso acontece com regularidade em units do systemd, porque estas trazem um ambiente próprio e mínimo, e o seu JAVA_HOME definido no .bashrc ali simplesmente não existe.

Ler a versão class diretamente do ficheiro JAR

Por vezes quer saber que Java um ficheiro exige antes sequer de o arrancar. Isso consegue-se sem instalar ferramentas adicionais. Cada ficheiro .class começa com a assinatura CAFEBABE, seguida de dois bytes de versão minor e dois bytes de versão major. O oitavo byte é, portanto, o número que procura:

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

O resultado é então algo como 202 254 186 190 0 0 0 65. Os primeiros quatro números são a assinatura e o último é a sua resposta: 65, ou seja, Java 21. Noutros programas substitui o caminho da classe em conformidade; o nome certo é dado por unzip -l server.jar ou pela entrada Main-Class em META-INF/MANIFEST.MF. Se tiver um JDK instalado, há uma forma mais cómoda:

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

Nos plugins este truque é especialmente útil. Se um único plugin interromper o arranque do servidor, verifique a respetiva classe principal e fica logo a saber se o plugin é demasiado recente para o seu núcleo de servidor ou o contrário.

Instalar a versão de Java adequada

É aqui que as distribuições se separam claramente, e é exatamente neste ponto que os guias genéricos falham. A situação em sistemas reais, verificada em julho de 2026:

PacoteDebian 13Debian 12Ubuntu 24.04Ubuntu 22.04
openjdk-8-jre-headlessnão incluídonão incluído8u4928u492
openjdk-11-jre-headlessnão incluídonão incluído11.0.3111.0.31
openjdk-17-jre-headlessnão incluído17.0.1917.0.1917.0.19
openjdk-21-jre-headless21.0.11não incluído21.0.1121.0.11
openjdk-25-jre-headless25.0.3não incluído25.0.325.0.3

Leia esta tabela uma vez com calma, poupa-lhe muito tempo. O Debian 12 conhece exclusivamente o Java 17; o Debian 13 já não conhece o Java 17, apenas o 21 e o 25. Quem quiser correr um servidor Minecraft 1.21 no Debian 12, ou no Debian 13 um programa com class file version 61.0, não encontra nada de adequado nas fontes padrão. O Ubuntu é mais generoso neste ponto e fornece tudo, do Java 8 ao 25, a partir da mesma fonte.

Antes de instalar, verifique se o pacote está sequer disponível:

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

Se em Candidato ou Candidate aparecer (nenhum), o pacote não existe na sua versão. Caso contrário, instale-o. Para apenas executar basta o JRE; o pacote -headless poupa as dependências gráficas e é sempre a escolha certa num servidor:

apt install -y openjdk-21-jre-headless

Quem compila código próprio ou usa o Gradle ou o Maven precisa do JDK em vez do JRE, ou seja, do openjdk-21-jdk-headless. Caminhos mais detalhados são descritos nos nossos artigos sobre Java 17 no Debian e Java 21 no Debian.

Quando a distribuição não oferece a versão: Adoptium Temurin

Para todos os casos que as fontes padrão não cobrem existe o repositório da Adoptium. Fornece o Temurin 8 a 26 para Debian 12, Debian 13, Ubuntu 22.04 e Ubuntu 24.04 e é, por isso, a única solução que disponibiliza em cada um destes sistemas todas as versões necessárias. Importante: o antigo caminho através do apt-key está descontinuado; hoje a chave pertence a /etc/apt/keyrings/ e é associada, através de signed-by, exatamente a esta única fonte.

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

A chamada ao awk insere automaticamente o nome de código correto, ou seja, trixie, bookworm, noble ou jammy. Se o apt update abortar depois disso com Conflicting values set for option Signed-By, já existe uma fonte Adoptium mais antiga, normalmente criada através do extrepo. Procure-a em /etc/apt/sources.list.d/ e remova o ficheiro duplicado.

AlmaLinux, Rocky Linux e RHEL

Na família Red Hat os pacotes têm outros nomes e trazem a versão no nome, sem o prefixo openjdk no início:

dnf install -y java-21-openjdk-headless

O AlmaLinux 9, o Rocky Linux 9 e o Oracle Linux 9 disponibilizam a série completa, de java-1.8.0-openjdk-headless até java-25-openjdk-headless. Já o AlmaLinux 10 fez o mesmo corte que o Debian 13 e conhece apenas o 21 e o 25: ali um dnf install java-17-openjdk-headless termina com No match for argument.

Também a ferramenta para trocar de versão se chama aqui simplesmente alternatives, e o update-alternatives é apenas um symlink para ela. Mas não se comporta da mesma maneira: o alternatives --list não aceita argumentos. O update-alternatives --list java, habitual no Debian, limita-se ali a mostrar o texto de ajuda e termina com o código de saída 2. Use alternatives --list | grep java ou alternatives --display java; este último indica então java - status is auto. em vez da forma do Debian java - auto mode.

Na família Red Hat os caminhos de instalação incluem a versão completa do pacote no nome do diretório, por exemplo /usr/lib/jvm/java-21-openjdk-21.0.11.0.10-1.el9.x86_64, e não a forma curta do Debian com sufixo de arquitetura. Só o AlmaLinux 10 cria adicionalmente o symlink curto java-21-openjdk. Não copie o caminho daqui, retire-o antes de alternatives --display java.

Um comportamento particularmente traiçoeiro: depois de instalar o Java 17 ao lado de um Java 21 já existente, o alternatives no AlmaLinux 9 mudou por iniciativa própria o link /usr/bin/java para a versão mais antiga, a 17. Ou seja, quem instala a segunda versão apenas para a usar de forma dirigida numa aplicação antiga acaba por alterar sem querer o Java padrão de todo o sistema. Depois de cada instalação adicional, defina explicitamente alternatives --set java <Caminho> e confirme com java -version.

Ativar a versão certa

Instalado não significa ativo. Depois de instalar um segundo JDK, o java -version mostra muitas vezes na mesma a versão antiga, porque o symlink /usr/bin/java permanece inalterado. No Debian e no Ubuntu isso é gerido pelo mecanismo alternatives:

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

O segundo comando mostra uma lista numerada e pergunta pela sua escolha. A partir daí a seleção fica em manual e deixa de ser sobreposta por futuras instalações de pacotes, que é precisamente o que se pretende. Quem quiser fazer isto num script, sem perguntas, usa a variante set com o caminho completo:

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

Defina adicionalmente o JAVA_HOME se houver ferramentas de build envolvidas. O Gradle e o Maven ignoram o symlink e orientam-se por esta variável:

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

Tenha em conta que um export só vale para a sessão em curso. Para serviços não ajuda de todo: no reinício seguinte, o serviço volta a usar a versão antiga do Java, porque nunca chegou a ver a variável. De forma permanente, o JAVA_HOME pertence por isso à unit do systemd, como Environment=, ou, para todo o sistema, ao /etc/environment.

O passo mais importante no caso dos serviços

Se a sua aplicação correr como serviço do systemd, o symlink do alternatives resolve apenas metade do problema. Um serviço arranca com um ambiente próprio e, se aí estiver um caminho absoluto antigo em ExecStart, nenhum update-alternatives do mundo muda isso. Indique o caminho completo, para que a versão fique fixada independentemente do estado do sistema:

[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

A seguir é imprescindível recarregar, caso contrário continua a valer a definição antiga:

systemctl daemon-reload
systemctl restart minecraft

O aspeto completo de uma unit destas é mostrado nos nossos artigos criar um serviço systemd e iniciar um servidor Minecraft automaticamente. A mesma armadilha aplica-se a scripts de arranque que correm com screen ou tmux, bem como a painéis como o Pterodactyl, que definem a versão do Java na imagem do container e não no sistema host.

O caso inverso: o Java é demasiado recente

Bastante mais raro, mas também mais confuso, é o caso contrário. Arranca um modpack antigo ou uma ferramenta de build antiga num servidor acabado de instalar com Java 21 e recebe a frase curta Unsupported class file major version 65, apesar de estar tudo atualizado. Aqui é uma biblioteca a comunicar que não consegue lidar com o formato de bytecode do seu novo ambiente de execução. Os causadores típicos são modpacks Forge para a 1.12.2, versões antigas do Gradle e carregadores de plugins que já têm muitos anos.

A solução não passa por remover o Java novo. Instale antes a versão antiga em paralelo e invoque-a de forma dirigida com o caminho absoluto. É exatamente para isso que o mecanismo alternatives foi construído, e é exatamente por isso que compensa manter o Java 8 e o Java 21 ao mesmo tempo num sistema. No Ubuntu isso faz-se diretamente a partir das fontes padrão, no Debian através do Temurin:

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

No script de arranque do serviço afetado substitui então o java por este caminho completo. O Java padrão de todo o sistema fica intacto e todas as outras aplicações continuam a funcionar como antes.

Se, mesmo depois da mudança, continuar a não arrancar

O erro de versão desapareceu, mas o servidor continua a não arrancar. Isso é normal e tem quase sempre uma de três causas.

Flags de arranque antigas. Quem passa do Java 8 para o 17 ou o 21 arrasta muitas vezes parâmetros de arranque que já não existem. O clássico é o antigo garbage collector, removido no Java 14:

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

Remova o -XX:+UseConcMarkSweepGC e todas as opções CMS associadas do comando de arranque. As versões modernas do Java usam o G1 por predefinição e, para servidores Minecraft, as flags Aikar atuais são a melhor base. A mensagem indica sempre pelo nome a opção problemática, por isso não precisa de adivinhar.

Memória insuficiente. Se depois da mudança aparecer Could not reserve enough space for object heap, o valor a seguir a -Xmx é maior do que a memória livre. As versões mais recentes do Java respeitam os limites de container e de cgroup de forma mais rigorosa do que o Java 8. Verifique a memória livre e, se for caso disso, leia o nosso artigo sobre swap e out of memory.

Já não há Java no sistema alternatives. Quem desinstala a versão antiga com demasiado zelo recebe update-alternatives: error: no alternatives for java ou simplesmente command not found. Isso resolve-se num minuto com a reinstalação de um pacote JRE qualquer, sem que nada se perca. Cuidado apenas com o apt autoremove quando outros pacotes assentam no default-jre: verifique a lista dos pacotes a remover antes de confirmar.

Como reconhece que resultou

Não se fie na simples ausência da mensagem de erro; verifique antes três pontos, um a seguir ao outro.

Primeiro, a versão na sua shell:

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

Segundo, a versão com que o processo corre realmente. O olhar sobre o /proc mostrado acima é aqui a ferramenta mais honesta, porque não confia nem em variáveis de ambiente nem em symlinks. Se o ls -l /proc/PID/exe apontar para o diretório da JVM pretendida, a mudança chegou mesmo ao destino.

Terceiro, o log da aplicação. Num servidor Minecraft, a linha Done (12.345s)! For help, type "help" é a prova de que o arranque decorreu por completo. Num serviço do systemd verifica isso com:

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

Se quiser saber com toda a exatidão, também pode levar a JVM a mostrar a sua própria configuração. Isso é prático quando há várias instalações de Java envolvidas e precisa de identificar uma delas sem ambiguidade:

java -XshowSettings:properties -version

No resultado encontra, entre outros, o java.home e o java.version. Com isso fica definitivamente esclarecida a questão de saber que instalação está a trabalhar.

Em resumo

O número no erro não é um código de erro, mas uma indicação de versão: major menos 44 dá a versão do Java, 52 é Java 8, 55 é Java 11, 61 é Java 17 e 65 é Java 21. O texto longo com has been compiled by a more recent version significa que o seu Java é demasiado antigo; o texto curto Unsupported class file major version, sem mais contexto, aponta normalmente para um Java demasiado recente. Instale a versão adequada, tenha presente que o Debian 12 traz apenas o Java 17 e o Debian 13 apenas o Java 21 e o 25 nas fontes padrão, e ative depois a versão a sério, na dúvida com caminho absoluto na unit do systemd. Para controlo basta um olhar ao /proc/PID/exe: assim fica a saber com certeza, e não por aproximação, que Java executa a sua aplicação.

Se vai instalar um servidor do zero e quer evitar estas armadilhas à partida, os nossos artigos sobre a configuração de um novo servidor root e a instalação de um servidor Minecraft no Debian ajudam a começar bem.

Perguntas frequentes

O que significa exatamente "class file version 65.0"?
A aplicação foi compilada com o Java 21. A regra é a versão major menos 44: 65 menos 44 dá 21. O segundo número da mensagem indica a versão mais alta que o ambiente de execução instalado compreende. Se aí estiver 61, no seu sistema corre o Java 17.
De que versão do Java preciso para o meu servidor Minecraft?
Até à 1.16.5, inclusive, Java 8; para a 1.17.x, Java 16; da 1.18 até à 1.20.4, Java 17; e a partir da 1.20.5, bem como para toda a série 1.21, Java 21. Isso corresponde às class file versions 52, 60, 61 e 65.
Instalei o Java 21, mas o java -version continua a mostrar o Java 17. Porquê?
A instalação não altera automaticamente o symlink /usr/bin/java. Mude com update-alternatives --config java. Se a aplicação correr como serviço, verifique também o caminho em ExecStart na unit do systemd, porque aí a versão está muitas vezes fixada de forma rígida.
Porque não existe o openjdk-21-jre-headless no Debian 12?
O Debian 12 traz nas fontes padrão exclusivamente o Java 17, ao passo que o Debian 13 traz apenas o Java 21 e o 25. Se precisar de uma versão que a sua distribuição não oferece, use o repositório da Adoptium, que disponibiliza o Temurin 8 a 26 para as quatro versões correntes de Debian e Ubuntu.
O erro é apenas "Unsupported class file major version 65", sem mais texto. O que fazer?
Esta forma curta não vem normalmente da JVM, mas de uma biblioteca como o ASM, que lê o bytecode. Neste caso o seu Java é demasiado recente para o software. Instale em paralelo a versão mais antiga adequada e arranque a aplicação afetada de forma dirigida com o caminho absoluto dessa versão.
Como descubro que Java está a ser usado por um processo já em execução?
Através do symlink exe no sistema de ficheiros proc: ls -l /proc/$(pgrep -f server.jar | head -n 1)/exe mostra o binário realmente executado, independentemente do caminho de pesquisa, do JAVA_HOME e da configuração do alternatives.
Depois de passar para o Java 21, o servidor arranca com "Unrecognized VM option". O que aconteceu?
O seu comando de arranque contém parâmetros que já não existem nas versões mais recentes do Java, tipicamente o -XX:+UseConcMarkSweepGC. Este garbage collector foi removido no Java 14. Retire do comando de arranque a opção indicada, que a mensagem de erro nomeia sempre.

Java Minecraft Debian Ubuntu Resolução de problemas Servidor de jogos OpenJDK Temurin