Solucionar el error de Java "Unsupported class file major version"
El número del error lo dice todo: 52 es Java 8, 55 es Java 11, 61 es Java 17 y 65 es Java 21. Así averiguas qué Java se ejecuta de verdad y dejas activa la versión correcta.
Arrancas un servidor de Minecraft, un plugin o una herramienta de compilación y, en lugar del inicio esperado, en el terminal aparece un número que a primera vista no dice nada: class file version 65.0. La buena noticia es que ese número ya contiene el diagnóstico completo. Solo hay que saber leerlo. Este artículo muestra cómo deducir del número la versión de Java necesaria, cómo averiguar qué Java se ejecuta realmente en tu servidor (a menudo no es el que crees) y cómo dejar activa de forma permanente la versión correcta.
Dos mensajes de error distintos, dos causas distintas
El texto exacto es decisivo, porque detrás de los dos mensajes habituales se esconden problemas justamente opuestos. El primero viene de la propia 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
Este mensaje significa siempre lo mismo: la aplicación se compiló con un Java más nuevo que el que tienes instalado. El primer número es lo que trae la aplicación, el segundo es lo que entiende tu entorno de ejecución. En el ejemplo de arriba el software exige Java 21 y lo que está instalado es Java 17.
La segunda variante se parece, pero viene de otro sitio:
java.lang.IllegalArgumentException: Unsupported class file major version 65
Esta forma breve no la lanza la JVM, sino una biblioteca que lee y analiza bytecode, casi siempre ASM. Aparece con Gradle, con cargadores de plugins antiguos y con núcleos de servidor veteranos. Aquí la situación suele ser la inversa: tu Java es demasiado nuevo para el software que quiere leer el bytecode. Quien confunde ambos mensajes instala en la dirección equivocada y luego se extraña de que el fallo siga ahí. Quédate con esta regla práctica: si aparece la frase larga con has been compiled by a more recent version, necesitas un Java más nuevo. Si solo aparece la frase corta, probablemente necesites uno más antiguo.
Descifrar el número: versión mayor menos 44
La conversión es más sencilla de lo que la presentan la mayoría de las guías. La versión mayor del class file menos 44 da la versión de Java. 65 menos 44 es 21, y ya está. La regla se cumple sin excepciones desde Java 1.1 y tampoco va a cambiar en el futuro, porque cada nueva versión principal de Java incrementa el número exactamente en uno.
| Class file version | Versión de Java | Dónde aparece habitualmente |
|---|---|---|
| 52 | Java 8 | Minecraft hasta 1.16.5, modpacks antiguos de Forge, software empresarial veterano |
| 53 | Java 9 | poco frecuente, versión de transición |
| 55 | Java 11 | muchas bibliotecas, aplicaciones Spring antiguas |
| 60 | Java 16 | Minecraft 1.17.x |
| 61 | Java 17 | Minecraft de 1.18 a 1.20.4, muchísimos plugins actuales |
| 65 | Java 21 | Minecraft a partir de 1.20.5, es decir, toda la serie 1.21, núcleos de servidor modernos |
| 69 | Java 25 | builds recién salidos, versión LTS actual |
Los valores intermedios siguen la misma regla: 62 es Java 18, 63 es Java 19, 64 es Java 20, 66 es Java 22 y así sucesivamente. El .0 que va detrás del número es la versión menor y en la práctica casi nunca importa.
Qué significa esto en concreto para Minecraft
La correspondencia para servidores de Minecraft es inequívoca y se puede aprender de memoria. Hasta 1.16.5 incluida vale Java 8, para 1.17.x hace falta como mínimo Java 16, de 1.18 a 1.20.4 como mínimo Java 17 y a partir de 1.20.5 (y por tanto para toda la serie 1.21) Java 21. Así que, si en el log aparece class file version 65.0 y acabas de actualizar a una versión reciente, ya tienes la causa localizada antes incluso de mirar la configuración.
¿Qué Java se está ejecutando realmente?
El error de razonamiento más habitual con esta clase de fallos es dar por hecho que la salida de java -version en tu sesión SSH vale también para el servicio en ejecución. Muchas veces no es así. Aun así, empieza por aquí:
java -version
La salida indica la versión en la primera línea, por ejemplo openjdk version "21.0.11" 2026-04-15. Si en su lugar aparece bash: java: command not found, no hay ningún Java en la ruta de búsqueda y la aplicación se arranca desde un script de inicio con ruta absoluta. Comprueba qué entornos de ejecución hay instalados:
ls /usr/lib/jvm
readlink -f "$(command -v java)"
update-alternatives --display java
readlink -f resuelve la cadena de enlaces simbólicos y te muestra el binario que se ejecuta realmente, por ejemplo /usr/lib/jvm/java-17-openjdk-amd64/bin/java. Eso importa más que el número de versión por sí solo, porque vas a necesitar esa ruta más adelante.
La línea del medio está escrita a propósito con command -v y no con el más extendido which. which es un programa aparte y no viene incluido en la instalación mínima de AlmaLinux 9 y 10, Rocky Linux 9 y Oracle Linux 9. Allí la variante con which falla dos veces: primero which: command not found y después readlink: missing operand. command -v forma parte de la propia shell y funciona en todas partes.
Para un proceso que ya está en marcha existe una vía que no deja lugar a dudas. Responde a la pregunta de con qué Java se inició realmente el servicio, con independencia de la ruta de búsqueda y de las variables de entorno:
ls -l /proc/$(pgrep -f server.jar | head -n 1)/exe
El enlace simbólico exe apunta al archivo ejecutable del proceso. Si ahí figura una ruta distinta de la esperada, ya tienes la causa: el servicio arranca con un Java diferente al de tu shell. Esto ocurre con regularidad en las unidades de systemd, porque traen un entorno propio y mínimo, y tu JAVA_HOME del .bashrc allí sencillamente no existe.
Leer la versión del class file directamente del archivo JAR
A veces quieres saber qué Java exige un archivo antes incluso de arrancarlo. Se puede hacer sin instalar herramientas adicionales. Todo archivo .class empieza con la firma CAFEBABE, seguida de dos bytes de versión menor y dos bytes de versión mayor. El octavo byte es, por tanto, el número que buscas:
unzip -p server.jar net/minecraft/bundler/Main.class | od -An -tu1 -N8
La salida es entonces algo parecido a 202 254 186 190 0 0 0 65. Los cuatro primeros números son la firma y el último es tu respuesta: 65, es decir, Java 21. Con otros programas sustituye la ruta de la clase según corresponda; el nombre adecuado te lo da unzip -l server.jar o la entrada Main-Class en META-INF/MANIFEST.MF. Si hay un JDK instalado, también existe una vía más cómoda:
javap -verbose -cp server.jar net.minecraft.bundler.Main | grep major
Con los plugins este truco resulta especialmente útil. Si un único plugin aborta el arranque del servidor, revisa su clase principal y sabrás al instante si el plugin es demasiado nuevo para el núcleo de tu servidor o al revés.
Instalar la versión de Java adecuada
Aquí las distribuciones se separan con claridad, y las guías genéricas fracasan justo en este punto. El estado en sistemas reales, comprobado en julio de 2026:
| Paquete | Debian 13 | Debian 12 | Ubuntu 24.04 | Ubuntu 22.04 |
|---|---|---|---|---|
| openjdk-8-jre-headless | no incluido | no incluido | 8u492 | 8u492 |
| openjdk-11-jre-headless | no incluido | no incluido | 11.0.31 | 11.0.31 |
| openjdk-17-jre-headless | no incluido | 17.0.19 | 17.0.19 | 17.0.19 |
| openjdk-21-jre-headless | 21.0.11 | no incluido | 21.0.11 | 21.0.11 |
| openjdk-25-jre-headless | 25.0.3 | no incluido | 25.0.3 | 25.0.3 |
Lee esta tabla con calma una vez, te va a ahorrar mucho tiempo. Debian 12 solo conoce Java 17; Debian 13 ya no conoce Java 17, sino únicamente 21 y 25. Así que quien quiera operar un servidor de Minecraft 1.21 en Debian 12, o un programa con class file version 61.0 en Debian 13, no va a encontrar nada apropiado en los repositorios estándar. Ubuntu es más generoso en este aspecto y lo entrega todo, de Java 8 a 25, desde un mismo sitio.
Comprueba antes de instalar si el paquete está disponible siquiera:
apt update
apt-cache policy openjdk-21-jre-headless
Si en Candidato, o Candidate en un sistema en inglés, aparece (ninguno), ese paquete no existe en tu versión. En caso contrario, instálalo. Para la simple ejecución basta con el JRE; el paquete -headless ahorra las dependencias gráficas y en un servidor es siempre la elección correcta:
apt install -y openjdk-21-jre-headless
Quien compile por su cuenta o use Gradle o Maven necesita el JDK en lugar del JRE, es decir, openjdk-21-jdk-headless. Nuestros artículos sobre Java 17 en Debian y Java 21 en Debian describen vías más detalladas.
Cuando la distribución no ofrece la versión: Adoptium Temurin
Para todos los casos que los repositorios estándar no cubren existe la fuente de paquetes de Adoptium. Suministra Temurin de la 8 a la 26 para Debian 12, Debian 13, Ubuntu 22.04 y Ubuntu 24.04, y es por tanto la única solución que proporciona en cualquiera de esos sistemas todas las versiones que puedas necesitar. Importante: la vía habitual de antes a través de apt-key está obsoleta; hoy la clave va en /etc/apt/keyrings/ y se asocia mediante signed-by exactamente a esa única fuente.
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 llamada a awk inserta automáticamente el nombre en clave correcto, es decir, trixie, bookworm, noble o jammy. Si después apt update aborta con Conflicting values set for option Signed-By, es que ya existe una fuente de Adoptium más antigua, creada normalmente a través de extrepo. Búscala en /etc/apt/sources.list.d/ y elimina el archivo duplicado.
AlmaLinux, Rocky Linux y RHEL
En la familia de Red Hat los paquetes se llaman de otra forma y llevan la versión en el nombre, sin el prefijo openjdk al principio:
dnf install -y java-21-openjdk-headless
AlmaLinux 9, Rocky Linux 9 y Oracle Linux 9 mantienen la serie completa, desde java-1.8.0-openjdk-headless hasta java-25-openjdk-headless. AlmaLinux 10, en cambio, ha hecho el mismo corte que Debian 13 y solo conoce ya 21 y 25; allí un dnf install java-17-openjdk-headless termina con No match for argument.
También la herramienta para cambiar de versión se llama aquí simplemente alternatives, y update-alternatives no es más que un enlace simbólico hacia ella. Sin embargo, no se comporta igual: alternatives --list no acepta ningún argumento. El update-alternatives --list java al que te acostumbra Debian allí solo imprime el texto de ayuda y termina con código de retorno 2. Usa alternatives --list | grep java o alternatives --display java; este último informa entonces con java - status is auto. en lugar de la forma de Debian java - auto mode.
Las rutas de instalación llevan en la familia de Red Hat la versión completa del paquete en el nombre del directorio, por ejemplo /usr/lib/jvm/java-21-openjdk-21.0.11.0.10-1.el9.x86_64, y no la forma corta de Debian con sufijo de arquitectura. Solo AlmaLinux 10 crea además el enlace simbólico corto java-21-openjdk. Así que no copies la ruta a mano, sácala de alternatives --display java.
Hay un comportamiento especialmente traicionero aquí: tras instalar Java 17 junto a un Java 21 ya presente, alternatives cambió por su cuenta en AlmaLinux 9 el enlace /usr/bin/java a la versión más antigua, la 17. Es decir, quien instala la segunda versión solo para usarla de forma puntual con una aplicación heredada, está cambiando sin querer el Java predeterminado de todo el sistema. Después de cada instalación adicional, define expresamente alternatives --set java <ruta> y comprueba el resultado con java -version.
Activar realmente la versión correcta
Instalado no significa activo. Tras instalar un segundo JDK, java -version sigue mostrando a menudo la versión antigua, porque el enlace simbólico /usr/bin/java permanece intacto. En Debian y Ubuntu esto lo regula el mecanismo de alternatives:
update-alternatives --list java
update-alternatives --config java
El segundo comando muestra una lista numerada y pregunta por tu elección. Después la selección queda en manual y ya no la sobrescriben futuras instalaciones de paquetes, que es exactamente lo que se busca. Quien quiera automatizarlo sin preguntas usa la variante set con la ruta completa:
update-alternatives --set java /usr/lib/jvm/temurin-21-jdk-amd64/bin/java
Define además JAVA_HOME si hay herramientas de compilación de por medio. Gradle y Maven ignoran el enlace simbólico y se guían por esta variable:
export JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64
$JAVA_HOME/bin/java -version
Ten en cuenta que un export solo vale en la sesión en curso. Para los servicios no sirve absolutamente de nada: tras el siguiente reinicio, el servicio vuelve a tomar la versión antigua de Java, porque nunca ha visto esa variable. De forma permanente, JAVA_HOME va por eso en la unidad de systemd como Environment= o, para todo el sistema, en /etc/environment.
El paso más importante en los servicios
Si tu aplicación se ejecuta como servicio de systemd, el enlace simbólico de alternatives es solo la mitad del trabajo. Un servicio arranca con su propio entorno y, si allí hay una ruta absoluta antigua en ExecStart, no hay update-alternatives en el mundo que cambie nada. Escribe la ruta completa para que la versión quede fijada con independencia del estado 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
Después hay que recargar sin falta, si no se sigue aplicando la definición antigua:
systemctl daemon-reload
systemctl restart minecraft
Cómo es una unidad de este tipo al completo lo muestran nuestros artículos crear un servicio de systemd e iniciar un servidor de Minecraft automáticamente. La misma trampa se aplica a los scripts de inicio que corren bajo screen o tmux, así como a paneles como Pterodactyl, que fijan la versión de Java en la imagen del container y no en el sistema anfitrión.
El caso inverso: Java es demasiado nuevo
Bastante menos frecuente, pero más desconcertante, es el otro caso. Arrancas un modpack antiguo o una herramienta de compilación veterana en un servidor recién instalado con Java 21 y ves la frase corta Unsupported class file major version 65, a pesar de que todo está actualizado. Aquí una biblioteca avisa de que no sabe qué hacer con el formato de bytecode de tu nuevo entorno de ejecución. Los desencadenantes típicos son los modpacks de Forge para 1.12.2, las versiones antiguas de Gradle y los cargadores de plugins que han quedado desfasados.
La solución no es eliminar el Java nuevo. Instala en su lugar la versión antigua en paralelo e invócala de forma explícita con su ruta absoluta. Para eso está pensado precisamente el mecanismo de alternatives, y por eso merece la pena mantener Java 8 y Java 21 a la vez en un mismo sistema. En Ubuntu se puede directamente desde los repositorios estándar, en Debian a través de Temurin:
apt install -y temurin-8-jdk
/usr/lib/jvm/temurin-8-jdk-amd64/bin/java -version
En el script de inicio del servicio afectado sustituye entonces java por esa ruta completa. El Java predeterminado del sistema queda intacto y todas las demás aplicaciones siguen funcionando como hasta ahora.
Si después del cambio sigue sin arrancar
El error de versión ha desaparecido, pero el servidor sigue sin arrancar. Es normal y casi siempre tiene una de estas tres causas.
Flags de arranque antiguos. Quien pasa de Java 8 a 17 o 21 suele arrastrar parámetros de arranque que ya no existen. El clásico es el antiguo recolector de basura, que se eliminó en Java 14:
Unrecognized VM option 'UseConcMarkSweepGC'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
Elimina -XX:+UseConcMarkSweepGC y todas las opciones CMS asociadas del comando de arranque. Las versiones modernas de Java usan G1 de forma predeterminada y, para servidores de Minecraft, las flags actuales de Aikar son la mejor base. El mensaje siempre menciona la opción problemática por su nombre, así que no tienes que adivinar.
RAM insuficiente. Si tras el cambio aparece Could not reserve enough space for object heap, el valor que sigue a -Xmx es mayor que la memoria libre. Las versiones más recientes de Java respetan los límites de container y de cgroup con más rigor que Java 8. Comprueba la memoria libre y, si hace falta, lee nuestro artículo sobre swap y out of memory.
Ya no queda ningún Java en el sistema de alternatives. Quien desinstala la versión antigua con demasiado entusiasmo recibe update-alternatives: error: no alternatives for java o directamente command not found. Esto se repara en un minuto reinstalando cualquier paquete JRE, y no se pierde nada por el camino. Solo hay que tener cuidado con apt autoremove cuando otros paquetes dependen de default-jre: revisa la lista de paquetes que se van a eliminar antes de confirmar.
Cómo saber que ha funcionado
No te fíes de la ausencia del mensaje de error, comprueba tres puntos uno tras otro.
Primero, la versión en tu shell:
java -version 2>&1 | head -n 1
Segundo, la versión con la que se ejecuta realmente el proceso. La mirada a /proc que se ha mostrado arriba es aquí la herramienta más honesta, porque no confía ni en las variables de entorno ni en los enlaces simbólicos. Si ls -l /proc/PID/exe apunta al directorio de la JVM deseada, el cambio ha llegado de verdad.
Tercero, el log de la aplicación. En un servidor de Minecraft, la línea Done (12.345s)! For help, type "help" es la prueba de que el arranque se ha completado por entero. En un servicio de systemd lo compruebas así:
systemctl status minecraft
journalctl -u minecraft -n 50 --no-pager
Si quieres saberlo con todo detalle, también se puede hacer que la JVM imprima su propia configuración. Resulta práctico cuando hay varias instalaciones de Java en juego y necesitas identificar una de ellas sin ambigüedad:
java -XshowSettings:properties -version
En la salida encontrarás, entre otras cosas, java.home y java.version. Con eso queda resuelta definitivamente la pregunta de qué instalación está trabajando en este momento.
Resumen breve
El número del error no es un código de error, sino una indicación de versión: la versión mayor menos 44 da la versión de Java, 52 es Java 8, 55 es Java 11, 61 es Java 17 y 65 es Java 21. El texto largo con has been compiled by a more recent version significa que tu Java es demasiado antiguo; el texto corto Unsupported class file major version sin más contexto apunta normalmente a un Java demasiado nuevo. Instala la versión adecuada, ten presente que Debian 12 solo lleva Java 17 y Debian 13 solo Java 21 y 25 en los repositorios estándar, y activa después la versión de verdad, en caso de duda con ruta absoluta en la unidad de systemd. Para comprobarlo basta un vistazo a /proc/PID/exe: así sabes con certeza, y no de forma aproximada, qué Java ejecuta tu aplicación.
Si estás montando un servidor de cero y quieres evitar estas trampas desde el principio, nuestros artículos sobre la configuración de un nuevo servidor root y sobre la instalación de un servidor de Minecraft en Debian te ayudan a empezar con buen pie.
Preguntas frecuentes
¿Qué significa exactamente "class file version 65.0"?
¿Qué versión de Java necesito para mi servidor de Minecraft?
He instalado Java 21, pero java -version sigue mostrando Java 17. ¿Por qué?
¿Por qué en Debian 12 no existe openjdk-21-jre-headless?
El error dice solo "Unsupported class file major version 65", sin más texto. ¿Qué hago?
¿Cómo averiguo qué Java usa un proceso que ya está en marcha?
Tras cambiar a Java 21 el arranque falla con "Unrecognized VM option". ¿Qué ha pasado?
2026 KernelHost GmbH. Todos los derechos reservados. Esta guía está protegida por derechos de autor. Su publicación en otros sitios web, aunque sea de forma parcial o modificada, no está permitida sin nuestro consentimiento por escrito. Las citas con indicación de la fuente y un enlace son muy bienvenidas.

