Risolvere l'errore Java "Unsupported class file major version"

Pubblicato il 15 min di lettura

Il numero nell'errore dice già tutto: 52 è Java 8, 55 è Java 11, 61 è Java 17, 65 è Java 21. Ecco come scoprire quale Java gira davvero sul tuo server e come attivare la versione giusta.

Avvii un server Minecraft, un plugin o un tool di build e, al posto dell'avvio che ti aspettavi, nel terminale compare un numero che a prima vista non dice nulla: class file version 65.0. La buona notizia è che quel numero contiene già l'intera diagnosi. Devi solo saperlo leggere. Questo articolo ti mostra come ricavare dal numero la versione di Java necessaria, come scoprire quale Java gira davvero sul tuo server (spesso non è quello che pensi) e come attivare in modo stabile la versione giusta.

Due messaggi di errore diversi, due cause diverse

Il testo esatto è decisivo, perché dietro i due messaggi più comuni si nascondono problemi esattamente opposti. Il primo arriva dalla Java Virtual Machine stessa:

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

Questo messaggio significa sempre la stessa cosa: l'applicazione è stata compilata con un Java più recente di quello che hai installato. Il primo numero è quello che porta con sé l'applicazione, il secondo è quello che il tuo runtime riesce a capire. Nell'esempio qui sopra il software richiede Java 21, mentre è installato Java 17.

La seconda variante sembra simile, ma arriva da tutt'altra parte:

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

Questa forma breve non viene lanciata dalla JVM, ma da una libreria che legge e analizza il bytecode, di norma ASM. Compare con Gradle, con vecchi caricatori di plugin e con core di server datati. Qui la situazione è quasi sempre rovesciata: il tuo Java è troppo recente per il software che vuole leggere il bytecode. Chi confonde i due messaggi installa nella direzione sbagliata e poi si stupisce che l'errore resti. Tieni a mente questa regola pratica: se compare la frase lunga con has been compiled by a more recent version, ti serve un Java più recente. Se compare solo la frase breve, con ogni probabilità te ne serve uno più vecchio.

Decifrare il numero: major version meno 44

Il calcolo è più semplice di come lo presentano quasi tutte le guide. La major version del class file meno 44 dà la versione di Java. 65 meno 44 fa 21, punto. La regola vale senza eccezioni da Java 1.1 in poi e non cambierà nemmeno in futuro, perché ogni nuova versione principale di Java alza il numero esattamente di uno.

Class file versionVersione JavaDove la trovi di solito
52Java 8Minecraft fino alla 1.16.5, vecchi modpack Forge, software enterprise datato
53Java 9rara, versione di transizione
55Java 11molte librerie, applicazioni Spring meno recenti
60Java 16Minecraft 1.17.x
61Java 17Minecraft dalla 1.18 alla 1.20.4, moltissimi plugin attuali
65Java 21Minecraft dalla 1.20.5 in poi, quindi tutte le 1.21, core di server moderni
69Java 25build nuovissime, release LTS attuale

I valori intermedi seguono la stessa regola: 62 è Java 18, 63 è Java 19, 64 è Java 20, 66 è Java 22 e così via. Lo .0 dopo il numero è la minor version e in pratica non conta mai.

Che cosa significa in concreto per Minecraft

L'abbinamento per i server Minecraft è netto e si impara a memoria. Fino alla 1.16.5 compresa serve Java 8, per la 1.17.x almeno Java 16, dalla 1.18 alla 1.20.4 almeno Java 17, dalla 1.20.5 in poi (e quindi per tutta la serie 1.21) Java 21. Se nel log compare class file version 65.0 e hai appena aggiornato a una versione recente, la causa è trovata ancora prima di aprire la configurazione.

Quale Java gira davvero?

L'errore di ragionamento più frequente con questa classe di problemi è dare per scontato che l'output di java -version nella tua sessione SSH valga anche per il servizio in esecuzione. Spesso non è così. Parti comunque da qui:

java -version

La prima riga dell'output riporta la versione, per esempio openjdk version "21.0.11" 2026-04-15. Se invece compare bash: java: command not found, nel percorso di ricerca non c'è alcun Java e l'applicazione viene avviata da uno script con percorso assoluto. Verifica quali runtime sono installati:

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

readlink -f risolve la catena di symlink e ti mostra il binario realmente eseguito, per esempio /usr/lib/jvm/java-17-openjdk-amd64/bin/java. Questo conta più del semplice numero di versione, perché quel percorso ti servirà più avanti.

La riga centrale usa di proposito command -v e non il più diffuso which. which è un programma a sé e nell'installazione minima di AlmaLinux 9 e 10, Rocky Linux 9 e Oracle Linux 9 non è incluso. Lì la variante con which fallisce due volte: prima which: command not found, poi readlink: missing operand. command -v invece è integrato nella shell stessa e funziona ovunque.

Per un processo già in esecuzione c'è una strada che non lascia spazio a dubbi. Risponde alla domanda su quale Java abbia davvero avviato il servizio, indipendentemente dal percorso di ricerca e dalle variabili d'ambiente:

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

Il symlink exe punta al file eseguibile del processo. Se lì trovi un percorso diverso da quello atteso, hai la causa: il servizio parte con un Java diverso da quello della tua shell. Succede spesso con le unit systemd, perché portano con sé un ambiente proprio e ridotto, e il tuo JAVA_HOME definito nel .bashrc lì semplicemente non esiste.

Leggere la class version direttamente dal file JAR

A volte vuoi sapere quale Java richiede un file prima ancora di avviarlo. Si può fare senza installare tool aggiuntivi. Ogni file .class inizia con la firma CAFEBABE, seguita da due byte di minor version e due byte di major version. L'ottavo byte è quindi il numero che cerchi:

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

L'output è più o meno 202 254 186 190 0 0 0 65. I primi quattro numeri sono la firma, l'ultimo è la tua risposta: 65, quindi Java 21. Con altri programmi sostituisci il percorso della classe di conseguenza, e il nome giusto lo trovi con unzip -l server.jar oppure nella voce Main-Class di META-INF/MANIFEST.MF. Se hai un JDK installato, c'è anche una via più comoda:

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

Con i plugin questo trucco è particolarmente utile. Se un singolo plugin blocca l'avvio del server, controlla la sua classe principale e saprai subito se il plugin è troppo recente per il tuo core di server oppure il contrario.

Installare la versione di Java adatta

Qui le distribuzioni si differenziano parecchio ed è esattamente il punto in cui le guide generiche falliscono. Ecco la situazione rilevata su sistemi reali, verificata a luglio 2026:

PacchettoDebian 13Debian 12Ubuntu 24.04Ubuntu 22.04
openjdk-8-jre-headlessnon inclusonon incluso8u4928u492
openjdk-11-jre-headlessnon inclusonon incluso11.0.3111.0.31
openjdk-17-jre-headlessnon incluso17.0.1917.0.1917.0.19
openjdk-21-jre-headless21.0.11non incluso21.0.1121.0.11
openjdk-25-jre-headless25.0.3non incluso25.0.325.0.3

Leggi questa tabella con calma, ti farà risparmiare molto tempo. Debian 12 conosce soltanto Java 17, mentre Debian 13 non ha più Java 17 ma solo la 21 e la 25. Quindi chi vuole far girare un server Minecraft 1.21 su Debian 12, oppure un programma con class file version 61.0 su Debian 13, nelle fonti standard non trova nulla di adatto. Su questo punto Ubuntu è più generoso e fornisce tutto da Java 8 fino alla 25.

Prima di installare, verifica che il pacchetto sia davvero disponibile:

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

Se accanto a Candidato (in inglese Candidate) compare (nessuno), nella tua versione il pacchetto non esiste. Altrimenti puoi installarlo. Per la sola esecuzione basta la JRE, e il pacchetto -headless risparmia le dipendenze grafiche, quindi su un server è sempre la scelta giusta:

apt install -y openjdk-21-jre-headless

Se compili in proprio oppure usi Gradle o Maven, ti serve il JDK al posto della JRE, quindi openjdk-21-jdk-headless. Percorsi più dettagliati li descrivono i nostri articoli su Java 17 su Debian e Java 21 su Debian.

Se la distribuzione non offre la versione: Adoptium Temurin

Per tutti i casi che le fonti standard non coprono c'è il repository di Adoptium. Fornisce Temurin dalla 8 alla 26 per Debian 12, Debian 13, Ubuntu 22.04 e Ubuntu 24.04 ed è quindi l'unica soluzione che su ognuno di questi sistemi mette a disposizione qualsiasi versione ti serva. Importante: la vecchia strada tramite apt-key è deprecata, oggi la chiave va in /etc/apt/keyrings/ e viene associata con signed-by esattamente a quella singola 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

La chiamata ad awk inserisce automaticamente il nome in codice corretto, quindi trixie, bookworm, noble oppure jammy. Se poi apt update si interrompe con Conflicting values set for option Signed-By, esiste già una vecchia fonte Adoptium, di solito creata tramite extrepo. Cercala in /etc/apt/sources.list.d/ ed elimina il file doppione.

AlmaLinux, Rocky Linux e RHEL

Nella famiglia Red Hat i pacchetti hanno nomi diversi e portano la versione nel nome, senza il prefisso openjdk all'inizio:

dnf install -y java-21-openjdk-headless

AlmaLinux 9, Rocky Linux 9 e Oracle Linux 9 offrono la serie completa, da java-1.8.0-openjdk-headless fino a java-25-openjdk-headless. AlmaLinux 10 ha invece fatto lo stesso taglio di Debian 13 e conosce solo la 21 e la 25, tanto che lì un dnf install java-17-openjdk-headless finisce con No match for argument.

Anche il tool per cambiare versione qui si chiama semplicemente alternatives, e update-alternatives ne è solo un symlink. Il comportamento però non è identico: alternatives --list non accetta argomenti. Il update-alternatives --list java a cui sei abituato su Debian lì stampa solo il testo di aiuto e termina con codice di uscita 2. Usa alternatives --list | grep java oppure alternatives --display java, e quest'ultimo riporta java - status is auto. invece della forma Debian java - auto mode.

Nella famiglia Red Hat i percorsi di installazione contengono la versione completa del pacchetto nel nome della directory, quindi per esempio /usr/lib/jvm/java-21-openjdk-21.0.11.0.10-1.el9.x86_64, e non la forma breve di Debian con il suffisso di architettura. Solo AlmaLinux 10 crea in più il symlink breve java-21-openjdk. Non ricopiare quindi il percorso a mano, ricavalo da alternatives --display java.

Un comportamento particolarmente insidioso: dopo l'installazione di Java 17 accanto a un Java 21 già presente, su AlmaLinux 9 alternatives ha spostato da solo il link /usr/bin/java sulla versione più vecchia, la 17. Chi installa la seconda versione solo per usarla in modo mirato con una vecchia applicazione, cambia così senza volerlo il Java predefinito dell'intero sistema. Dopo ogni installazione aggiuntiva imposta quindi esplicitamente alternatives --set java <PERCORSO> e controlla il risultato con java -version.

Attivare davvero la versione giusta

Installata non vuol dire attiva. Dopo l'installazione di un secondo JDK, java -version mostra spesso ancora la vecchia versione, perché il symlink /usr/bin/java resta invariato. Su Debian e Ubuntu se ne occupa il meccanismo alternatives:

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

Il secondo comando mostra un elenco numerato e ti chiede quale versione scegliere. Dopo la scelta l'impostazione passa su manual e non viene più sovrascritta dalle installazioni di pacchetti successive, che è esattamente ciò che serve. Se vuoi metterlo in uno script senza domande, usa la variante set con il percorso completo:

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

Imposta anche JAVA_HOME se entrano in gioco tool di build. Gradle e Maven ignorano il symlink e si regolano su questa variabile:

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

Tieni presente che un export vale solo nella sessione corrente. Per i servizi non serve proprio a nulla: al riavvio successivo il servizio riprende la vecchia versione di Java, perché quella variabile non l'ha mai vista. In modo permanente JAVA_HOME va quindi nella unit systemd come Environment= oppure, a livello di intero sistema, in /etc/environment.

Il passaggio più importante con i servizi

Se la tua applicazione gira come servizio systemd, il symlink di alternatives è solo metà del lavoro. Un servizio parte con un ambiente proprio e, se in ExecStart è rimasto un vecchio percorso assoluto, nessun update-alternatives al mondo cambierà qualcosa. Inserisci il percorso completo, così la versione resta fissa a prescindere dallo stato del 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

Subito dopo ricarica assolutamente la configurazione, altrimenti resta attiva la vecchia definizione:

systemctl daemon-reload
systemctl restart minecraft

Come si presenta una unit completa lo mostrano i nostri articoli creare un servizio systemd e avviare automaticamente un server Minecraft. La stessa trappola vale per gli script di avvio che girano tramite screen o tmux, e per pannelli come Pterodactyl, che fissano la versione di Java nell'immagine del container e non sul sistema host.

Il caso opposto: Java troppo recente

Nettamente più raro, ma anche più disorientante, è il caso contrario. Avvii un modpack datato o un vecchio tool di build su un server appena installato con Java 21 e ti ritrovi davanti la frase breve Unsupported class file major version 65, anche se tutto è aggiornato. Qui una libreria segnala che non sa che farsene del formato bytecode del tuo nuovo runtime. Le cause tipiche sono i modpack Forge per la 1.12.2, versioni datate di Gradle e caricatori di plugin ormai avanti con gli anni.

La soluzione non è rimuovere il Java nuovo. Installa invece la vecchia versione in parallelo e richiamala in modo mirato con il percorso assoluto. Il meccanismo alternatives è pensato esattamente per questo, ed è proprio per questo che conviene tenere Java 8 e Java 21 contemporaneamente sullo stesso sistema. Su Ubuntu si fa direttamente dalle fonti standard, su Debian tramite Temurin:

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

Nello script di avvio del servizio interessato sostituisci poi java con questo percorso completo. Il Java predefinito di sistema resta intatto e tutte le altre applicazioni continuano a funzionare come prima.

Se dopo il cambio continua a non partire

L'errore di versione è sparito, ma il server continua a non avviarsi. È normale e quasi sempre la causa è una di tre.

Vecchi flag di avvio. Chi passa da Java 8 alla 17 o alla 21 si porta spesso dietro parametri che non esistono più. Il classico è il vecchio garbage collector, rimosso in Java 14:

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

Rimuovi -XX:+UseConcMarkSweepGC e tutte le opzioni CMS collegate dal comando di avvio. Le versioni moderne di Java usano G1 come impostazione predefinita e, per i server Minecraft, gli attuali flag di Aikar sono una base migliore. Il messaggio indica sempre per nome l'opzione che dà problemi, quindi non devi tirare a indovinare.

Troppo poca RAM. Se dopo il cambio compare Could not reserve enough space for object heap, il valore dopo -Xmx è più grande della memoria libera. Le versioni recenti di Java rispettano i limiti di container e cgroup in modo più rigido rispetto a Java 8. Controlla la memoria libera e, se necessario, leggi il nostro articolo su swap e out of memory.

Nessun Java nel sistema alternatives. Chi disinstalla la vecchia versione con troppo entusiasmo ottiene update-alternatives: error: no alternatives for java oppure semplicemente command not found. Si ripara in un minuto reinstallando un qualsiasi pacchetto JRE e non si perde nulla. L'unica accortezza riguarda apt autoremove quando altri pacchetti dipendono da default-jre: controlla l'elenco dei pacchetti da rimuovere prima di confermare.

Come capisci che ha funzionato

Non fidarti dell'assenza del messaggio di errore, controlla invece tre punti uno dopo l'altro.

Primo, la versione nella tua shell:

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

Secondo, la versione con cui il processo gira davvero. Lo sguardo su /proc mostrato sopra è lo strumento più onesto, perché non si fida né delle variabili d'ambiente né dei symlink. Se ls -l /proc/PID/exe punta alla directory JVM desiderata, il cambio è andato davvero a segno.

Terzo, il log dell'applicazione. Su un server Minecraft la riga Done (12.345s)! For help, type "help" è la prova che l'avvio è arrivato in fondo. Con un servizio systemd lo verifichi così:

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

Se vuoi la certezza assoluta, puoi anche far stampare alla JVM la sua stessa configurazione. Torna comodo quando sul sistema convivono più installazioni di Java e devi identificarne una senza ambiguità:

java -XshowSettings:properties -version

Nell'output trovi tra le altre cose java.home e java.version. Così la domanda su quale installazione stia effettivamente lavorando è chiarita in via definitiva.

In sintesi

Il numero nell'errore non è un codice di errore, ma un'indicazione di versione: major meno 44 dà la versione di Java, 52 è Java 8, 55 è Java 11, 61 è Java 17, 65 è Java 21. Il messaggio lungo con has been compiled by a more recent version significa che il tuo Java è troppo vecchio, mentre il testo breve Unsupported class file major version senza altro contesto indica di solito un Java troppo recente. Installa la versione adatta, tieni presente che nelle fonti standard Debian 12 offre solo Java 17 e Debian 13 solo Java 21 e 25, e poi attiva davvero quella versione, nel dubbio con il percorso assoluto nella unit systemd. Per il controllo basta un'occhiata a /proc/PID/exe, così sai con certezza e non solo all'incirca quale Java esegue la tua applicazione.

Se stai configurando un server da zero e vuoi evitare queste trappole fin dall'inizio, i nostri articoli sulla configurazione di un nuovo server root e sull'installazione di un server Minecraft su Debian ti aiutano a partire in modo pulito.

Domande frequenti

Che cosa significa esattamente "class file version 65.0"?
L'applicazione è stata compilata con Java 21. La regola è major version meno 44: 65 meno 44 fa 21. Il secondo numero del messaggio indica la versione più alta che il runtime installato riesce a capire. Se lì compare 61, sul tuo sistema gira Java 17.
Quale versione di Java serve per il mio server Minecraft?
Fino alla 1.16.5 compresa Java 8, per la 1.17.x Java 16, dalla 1.18 alla 1.20.4 Java 17 e dalla 1.20.5 in poi, quindi per tutta la serie 1.21, Java 21. Corrispondono alle class file version 52, 60, 61 e 65.
Ho installato Java 21, ma java -version mostra ancora Java 17. Perché?
L'installazione non sposta automaticamente il symlink /usr/bin/java. Cambia versione con update-alternatives --config java. Se l'applicazione gira come servizio, controlla anche il percorso in ExecStart della unit systemd, perché lì la versione è spesso scritta in modo fisso.
Perché su Debian 12 non esiste openjdk-21-jre-headless?
Nelle fonti standard Debian 12 offre soltanto Java 17, mentre Debian 13 solo Java 21 e 25. Se ti serve una versione che la tua distribuzione non fornisce, usa il repository di Adoptium, che mette a disposizione Temurin dalla 8 alla 26 per tutte e quattro le versioni comuni di Debian e Ubuntu.
L'errore è solo "Unsupported class file major version 65" senza altro testo. Che faccio?
Questa forma breve di solito non arriva dalla JVM, ma da una libreria come ASM che legge il bytecode. In questo caso il tuo Java è troppo recente per il software. Installa in parallelo la versione più vecchia adatta e avvia l'applicazione interessata richiamando espressamente il suo percorso assoluto.
Come scopro quale Java usa un processo già in esecuzione?
Tramite il symlink exe nel filesystem proc: ls -l /proc/$(pgrep -f server.jar | head -n 1)/exe mostra il binario realmente eseguito, indipendentemente dal percorso di ricerca, da JAVA_HOME e dalla configurazione di alternatives.
Dopo il passaggio a Java 21 il server si ferma con "Unrecognized VM option". Che cosa è successo?
Il tuo comando di avvio contiene parametri che nelle versioni recenti di Java non esistono più, tipicamente -XX:+UseConcMarkSweepGC. Questo garbage collector è stato rimosso in Java 14. Togli dal comando di avvio l'opzione indicata: il messaggio di errore la nomina sempre per nome.

Java Minecraft Debian Ubuntu Risoluzione dei problemi Gameserver OpenJDK Temurin