Saltar al contenido

Desarrollo web

Error 500 en todas las rutas tras activar un tema de WordPress: por qué ocurre y cómo se recupera

Se activa un tema en un WordPress en producción y, en cuestión de segundos, todas las direcciones empiezan a devolver error 500: la portada, las páginas internas, el panel de administración y la API REST. Es uno de los incidentes más desconcertantes que puede dar WordPress, porque el mensaje no dice nada y la herramienta con la que se arreglaría —el propio panel— también está caída. Esta guía explica por qué ocurre, por qué a veces entra en bucle, y en qué orden se recupera.

Diagrama del bucle: una visita dispara el gancho after_switch_theme, el código pide permisos que no existen sin sesión, la petición muere con error 500 y WordPress nunca borra la marca de tema recién cambiado, por lo que la siguiente petición repite lo mismo.
El fallo ocurre antes de que WordPress marque el cambio como completado, así que cada petición nueva repite el error.

Lo primero que se ve no dice nada

Un error 500 es, por definición, un error que el servidor no supo explicar. En el navegador solo hay una página en blanco o el aviso genérico de que «hubo un problema crítico». Nada sobre el tema, nada sobre el archivo, nada sobre la línea.

El primer instinto —entrar a wp-admin y desactivar lo último que se tocó— es justo el que no funciona, porque el panel devuelve exactamente el mismo error. Y ahí es donde se pierde el tiempo: intentando entrar por una puerta que también está rota.

Por qué cae todo, y no solo la parte pública

El functions.php de un tema se carga en todas las peticiones. WordPress lo incluye antes de decidir si quien llama viene a leer un artículo, a entrar al escritorio o a consultar la API. Un error fatal ahí dentro no distingue: se lleva por delante la portada, el panel y /wp-json/.

Esto tiene una consecuencia práctica que conviene interiorizar antes de necesitarla: si el fallo está en el tema activo, WordPress no puede arreglarse desde dentro de WordPress. Hay que entrar por debajo.

Y se lleva por delante algo más que las visitas de ese rato. Si el rastreador de Google pasa mientras el sitio devuelve 500, registra el error y reduce el ritmo con el que vuelve, así que la caída sigue costando cuando ya está resuelta. Es una de las razones por las que la disponibilidad forma parte del SEO técnico y no solo de la ficha del servidor.

La causa habitual: un gancho que corre cuando no hay nadie

El patrón que más veces provoca esto es un código razonable: al activar el tema, copiar su paleta de colores a los ajustes de un constructor de páginas, crear unas páginas de ejemplo o registrar unos menús. Se engancha donde parece lógico:

add_action( 'after_switch_theme', 'mi_tema_configuracion_inicial' );

El problema es cuándo corre ese gancho. No se dispara al pulsar «Activar»: se dispara en la siguiente petición al sitio. Y esa siguiente petición es, casi siempre, una visita anónima sin sesión iniciada.

Si la función pide a un plugin que guarde ajustes, ese plugin comprueba permisos, no encuentra ningún usuario que los tenga, y lanza una excepción del tipo «Access denied». Si nadie la captura, se convierte en error fatal.

Lo que lo convierte en un bucle

Aquí está la parte que alarga un incidente de dos minutos a casi una hora. WordPress guarda una marca temporal de «acabo de cambiar de tema» y la borra después de ejecutar el gancho. Si el gancho muere antes, la marca nunca se borra.

Resultado: cada petición nueva vuelve a ver la marca, vuelve a lanzar el gancho, vuelve a fallar. El sitio no se recupera solo ni esperando, porque cada intento de arreglarlo entrando al panel es, en sí mismo, otra petición que repite el error.

Cómo se corrige

La corrección tiene cuatro piezas, y las cuatro hacen falta:

  1. Mover el trabajo a donde sí hay sesión. En lugar de actuar en after_switch_theme, ese gancho solo deja una nota (update_option). El trabajo de verdad se hace en admin_init, que solo corre dentro del panel, donde por definición hay un usuario identificado.
  2. Borrar la nota ANTES de intentar la tarea, no después. Si algo vuelve a fallar, falla una sola vez y no se repite eternamente.
  3. Comprobar permisos explícitamente con current_user_can() antes de llamar a nada de un plugin.
  4. Envolver en try/catch toda llamada a la API de un plugin de terceros. Que ese plugin cambie de comportamiento en una actualización no puede tumbar el sitio.

La variante que aparece días después: la aridad

Hay un pariente cercano de este fallo que se manifiesta de otra forma: el sitio funciona con normalidad y de pronto una sola página —el 404, el buscador, una plantilla concreta— devuelve error 500. La causa suele ser la aridad de un gancho.

Esta familia de errores tiene una versión todavía más incómoda, la que no llega a tumbar nada: el código corre, no salta ninguna excepción, y aun así el dato no llega a su destino. Están reunidos en tres funciones de WordPress que descartan tu dato sin decir nada.

Cuando se registra una función en un filtro, se declara cuántos argumentos se quieren recibir. Si se piden más de los que el filtro entrega, PHP 8 lanza un ArgumentCountError —error fatal— en el momento en que ese filtro se dispara.

// wp_script_attributes pasa UN argumento, no dos:
add_filter( 'wp_script_attributes', 'mi_funcion', 10, 2 );  // revienta

Lo insidioso es que php -l da el visto bueno: la sintaxis es perfecta. Y como el filtro solo se dispara en ciertas páginas, el sitio puede funcionar durante días hasta que alguien llega a la ruta equivocada.

La corrección puntual es usar el filtro correcto —en ese ejemplo, script_loader_tag, que sí pasa el identificador del script—. Pero la corrección de fondo es otra: un guion que revise todos los ganchos del proyecto y compare los argumentos declarados con los que ese gancho pasa de verdad.

Lo que conviene dejar por escrito

  • Antes de activar nada en producción, hay que tener lista la vía de escape. El acceso al servidor por cPanel, FTP o SSH debe estar comprobado antes de pulsar el botón. Buscar la contraseña del hosting con el sitio caído es el peor momento posible para buscarla.
  • Un error fatal en el tema activo se lleva el panel. No se puede contar con desactivarlo desde WordPress.
  • Nada que corra en un gancho de activación debe suponer que hay sesión. Casi nunca la hay.
  • Borrar la marca antes de la tarea, no después. Es la diferencia entre un fallo y un bucle.
  • Comprobar siete rutas después de activar, no solo la portada: una página interna, el blog, una entrada, el 404, el buscador, wp-admin y /wp-json/.

Estas cuatro reglas no piden herramientas nuevas ni tiempo extra: piden acordarse de ellas justo antes de pulsar «Activar», que es cuando cuestan menos y valen más. Si el sitio que hay que mover está en producción y no hay margen para una caída, esta es una de las cosas que se revisan en un proyecto de desarrollo web antes de tocar nada.

Preguntas frecuentes

¿Por qué un error en el tema deja inaccesible también el panel de administración?

Porque el functions.php del tema se carga en todas las peticiones, no solo en las páginas públicas. WordPress lo incluye antes de decidir si la visita viene al blog, al escritorio o a la API REST. Si ahí dentro se lanza un error fatal, cae todo por igual: portada, wp-admin y /wp-json/. Por eso no se puede entrar a desactivar el tema desde WordPress: el propio WordPress es lo que está roto.

¿Cómo se recupera un sitio que devuelve 500 en todas las rutas?

Hace falta una vía que no pase por WordPress. Se entra al servidor por cPanel, FTP o SSH y se renombra la carpeta del tema dentro de wp-content/themes/. Al no encontrarlo, WordPress vuelve solo a un tema por defecto y el panel revive. Lo mismo sirve para un plugin: renombrar su carpeta dentro de wp-content/plugins/. No hace falta tocar la base de datos.

¿Qué es la aridad de un hook y por qué rompe el sitio?

La aridad es cuántos argumentos pasa el gancho a la función enganchada. Si se registra una función pidiendo más argumentos de los que el gancho entrega, PHP 8 lanza un ArgumentCountError, que es un error fatal. Ocurre solo cuando ese gancho se dispara, así que el código puede pasar todas las pruebas y reventar en una página concreta. Es el caso de wp_script_attributes, que pasa un argumento aunque muchos ejemplos lo enganchen esperando dos.

¿Se puede detectar esto antes de publicar?

Sí, y es barato. php -l no lo detecta, porque la sintaxis es correcta. Lo que funciona es un guion que recorra el código, saque cada add_action y add_filter con su número de argumentos declarado, y lo compare con los que ese gancho pasa de verdad. Tarda un segundo y se ejecuta antes de cada despliegue.

¿Por qué el fallo aparece en la primera visita y no al pulsar «Activar»?

Porque after_switch_theme no se dispara al pulsar el botón, sino en la siguiente petición. Y esa suele ser una visita anónima, sin sesión iniciada. Todo lo que dentro de ese gancho necesite permisos de administrador va a fallar. Si además el error impide que WordPress marque el cambio como completado, el gancho vuelve a intentarlo en la petición siguiente, y en la siguiente: un bucle del que el sitio no sale solo.

Compartir

Cuéntanos qué quieres construir o automatizar

Media hora de conversación y te decimos qué se puede mejorar en tu operación, cuánto cuesta y en cuánto tiempo. Sin compromiso.

Solicitar diagnóstico