
Manejador global de errores en Vue 3: qué captura app.config.errorHandler y qué se le escapa
- vuetelemetry
- Guías
- 8 min de lectura
app.config.errorHandler se instala en tres líneas. El trabajo de verdad está en conocer las siete fuentes que cubre, entender por qué su tercer argumento se convierte en un código en producción, cómo onErrorCaptured puede silenciarlo y qué errores solo te contará el navegador.
Un componente lanza una excepción dentro de un watcher, en el teléfono de un cliente. En desarrollo, el mismo fallo habría llenado la pantalla con una traza de pila. En un build de producción, por defecto, Vue escribe el error en una consola que nadie está mirando, y la aplicación sigue adelante en el estado en que la dejó el fallo. Un manejador global de errores es la pieza que convierte esa línea silenciosa en algo que puedes recibir, contar y corregir.
Vue 3 ofrece uno para toda la aplicación: app.config.errorHandler. La referencia oficial de la API lo describe como un manejador global para los errores no capturados que se propagan desde dentro de la aplicación. Instalarlo lleva tres líneas. El trabajo de verdad consiste en saber qué cubre, qué deja fuera y qué contienen sus argumentos una vez compilado el código para producción.
Qué captura app.config.errorHandler

El manejador es una propiedad de la instancia de la aplicación: app.config.errorHandler = (err, instance, info) => { ... }. Va entre createApp() y app.mount(). La guía de Vue es explícita con el orden: hay que aplicar todas las configuraciones de la aplicación antes de montarla. El motivo se entiende bien aquí, porque el primer renderizado y el setup() del componente raíz se ejecutan ambos durante el montaje.
La referencia enumera siete fuentes de las que el manejador puede capturar errores: renderizados de componentes, manejadores de eventos, hooks del ciclo de vida, la función setup(), watchers, hooks de directivas personalizadas y hooks de transición. Conviene leer esa lista como un contrato. Describe el código que Vue llama por ti. El código que se ejecuta sin ninguna llamada de Vue por encima en la pila es otro asunto, y tiene su propia sección más abajo.
Tres argumentos y la trampa de producción
El manejador recibe tres argumentos. El primero es el error, tipado como unknown en la firma de TypeScript, porque JavaScript permite lanzar cualquier cosa: una cadena, un objeto plano, undefined. Comprueba que sea una instancia de Error antes de leer su mensaje o su pila. El segundo es la instancia del componente que provocó el error, o null. El tercero, info, es una cadena propia de Vue que nombra el tipo de fuente, por ejemplo en qué hook del ciclo de vida se lanzó el error.
- app.config.errorHandler recibe (err, instance, info) y se define antes de app.mount()
- Siete fuentes documentadas: renderizados de componentes, manejadores de eventos, hooks del ciclo de vida, setup(), watchers, hooks de directivas personalizadas, hooks de transición
- En los builds de producción, info es un código corto; la página Production Error Code Reference lo devuelve a texto
- Comportamiento por defecto: relanzar en desarrollo, escribir en consola en producción; throwUnhandledErrorInProduction (3.5+) cambia lo segundo
- Un hook onErrorCaptured() que devuelve false detiene el error antes del manejador global
- Fuera de Vue: el evento error de window para errores de script síncronos, unhandledrejection para las promesas
Ese tercer argumento guarda una sorpresa para quien abre un informe de producción por primera vez. En los builds de producción, info es un código abreviado en lugar de la cadena completa. La documentación de Vue mantiene una página Production Error Code Reference que relaciona cada código de ejecución con su texto original. Envía el valor en bruto tal como llega y haz la consulta al leer el informe. Una lógica que compare info con una cadena vista en desarrollo dejará de coincidir en cuanto la aplicación esté compilada.
Desarrollo y producción no se comportan igual
Sin manejador, lo que hace Vue depende del build. Según la referencia, el manejador por defecto vuelve a lanzar los errores en desarrollo y los registra en producción. La mitad de desarrollo es deliberada. La documentación dice que el error se lanza, y puede llegar a tumbar la aplicación, para que resulte más visible y así se detecte y se corrija.
La mitad de producción tiene un efecto secundario que importa para la monitorización. Un error que solo se escribe en la consola no se lanza, y la propia documentación señala que eso puede impedir que los servicios de monitorización de errores atrapen los que solo ocurren en producción. Desde Vue 3.5 existe un interruptor para ese caso: app.config.throwUnhandledErrorInProduction, un booleano cuyo valor por defecto es false. Con true, los errores no gestionados se lanzan también en modo producción.
onErrorCaptured: barreras locales antes del manejador global
El manejador global es la última parada. Antes de él, cualquier componente puede interceptar los errores que vienen de sus descendientes con onErrorCaptured(), o errorCaptured en la Options API. El hook recibe los mismos tres argumentos y es la herramienta para una barrera de errores: cambiar un estado, mostrar un contenido alternativo. La referencia añade una advertencia. El estado de error no debe renderizar el contenido original que causó el error; de lo contrario, el componente entra en un bucle de renderizado infinito.
La propagación sigue cuatro reglas documentadas. Por defecto, los errores capturados se envían igualmente a app.config.errorHandler si está definido, para poder reportarlos en un único lugar. Si hay varios hooks errorCaptured en la cadena de padres, se invocan todos, de abajo arriba, de forma parecida al burbujeo de los eventos del DOM. Si un hook lanza a su vez un error, tanto ese como el original van al manejador global. Y un hook puede devolver false para detener el error ahí: no se invocará ningún otro hook ni el manejador global para él. Esa última regla suele ser la explicación de un error que muestra un contenido alternativo en pantalla y nunca aparece en un informe.
Lo que el manejador global nunca ve
Ahora, la parte que queda fuera del contrato. Una función pasada a setTimeout, un listener añadido a mano con addEventListener, una cadena de promesas iniciada en un módulo de utilidades, un script de terceros: ninguno es una de las siete fuentes. El navegador tiene dos eventos para ellos. Según MDN, el evento error se dispara en window cuando un recurso no se ha podido cargar o usar, por ejemplo cuando un script tiene un error de ejecución, y solo se genera para errores de script lanzados de forma síncrona. Una promesa rechazada sin manejador de rechazo, lo que incluye un throw no capturado dentro de una función async, dispara en su lugar unhandledrejection.
Una instalación completa tiene, por tanto, tres escuchas que alimentan una sola función: app.config.errorHandler para el código que llama Vue, un listener de error en window para los fallos síncronos de otros sitios, y un listener de unhandledrejection para las promesas que nadie capturó. Si falta uno, toda una clase de fallos queda invisible.
Reportar sin empeorar las cosas
Lo que la función hace con un error decide si el manejador sirve de algo. Mantenla pequeña y defensiva: normaliza el error, adjunta info, la ruta actual y la versión desplegada, y envíalo a tu punto de recogida. Envuelve su cuerpo en un try y un catch, porque una función de reporte que falla solo añade un segundo fallo al primero. Elimina duplicados antes de enviar, ya que un error de renderizado dentro de una lista puede repetirse en cada fila. Y conserva una llamada a console.error en el manejador mientras desarrollas: una vez instalado el tuyo, el que Vue trae por defecto, el que vuelve a lanzar el error en desarrollo, deja de ejecutarse.
Los errores responden a una sola pregunta: qué se ha roto. No dicen cuánto esperó el usuario antes del fallo ni qué petición estaba en curso en ese momento. Ese es el terreno de las trazas, tratado en OpenTelemetry en una app Vue. Empieza de todos modos por el manejador. Son unas pocas líneas sin ninguna dependencia, y reporta los problemas que no sabías que había que buscar.
FAQ
¿Dónde se define el manejador global de errores en Vue 3?
En la instancia de la aplicación, entre createApp() y app.mount(): app.config.errorHandler = (err, instance, info) => { ... }. La guía de Vue pide aplicar todas las configuraciones de la aplicación antes de montarla.
¿app.config.errorHandler captura todos los errores de la página?
No. La referencia de Vue enumera siete fuentes: renderizados de componentes, manejadores de eventos, hooks del ciclo de vida, setup(), watchers, hooks de directivas personalizadas y hooks de transición. Los errores que surgen fuera de ellas los comunica el navegador, mediante el evento error de window para los errores de script síncronos y mediante unhandledrejection para las promesas rechazadas sin manejador.
¿Por qué el argumento info es un código y no una cadena legible en producción?
En los builds de producción, Vue pasa un código abreviado como tercer argumento de app.config.errorHandler y de onErrorCaptured. La página Production Error Code Reference de la documentación de Vue relaciona cada código de ejecución con su cadena de información original.
¿Qué diferencia hay entre onErrorCaptured y app.config.errorHandler?
onErrorCaptured es un hook de componente que captura los errores de los componentes descendientes, lo que lo convierte en la herramienta para un contenido alternativo local. app.config.errorHandler vale para toda la aplicación y recibe igualmente esos errores por defecto, salvo que un hook devuelva false para detener la propagación.



Una instalación completa tiene, por tanto, tres escuchas que alimentan una sola función: app.config.errorHandler para el código que llama Vue, un listener de error en window para los fallos síncronos de otros sitios, y un listener de unhandledrejection para las promesas que nadie capturó. Si falta uno, toda una clase de fallos queda invisible.