
Gestor global de erros no Vue 3: o que o app.config.errorHandler apanha e o que lhe escapa
- vuetelemetry
- Guias
- 8 min de leitura
O app.config.errorHandler instala-se em três linhas. O trabalho a sério é conhecer as sete origens que cobre, perceber porque é que o terceiro argumento passa a ser um código em produção, como o onErrorCaptured o pode silenciar e que erros só o browser lhe dará a conhecer.
Um componente lança uma exceção dentro de um watcher, no telemóvel de um cliente. Em desenvolvimento, a mesma falha teria enchido o ecrã com um stack trace. Num build de produção, por omissão, o Vue escreve o erro numa consola para a qual ninguém está a olhar, e a aplicação continua no estado em que a falha a deixou. Um gestor global de erros é a peça que transforma essa linha silenciosa em algo que se pode receber, contar e corrigir.
O Vue 3 fornece um para toda a aplicação: app.config.errorHandler. A referência oficial da API descreve-o como um gestor global para erros não apanhados que se propagam a partir do interior da aplicação. Instalá-lo leva três linhas. O trabalho a sério é saber o que cobre, o que deixa de fora e o que contêm os seus argumentos depois de o código ser compilado para produção.
O que o app.config.errorHandler apanha

O gestor é uma propriedade da instância da aplicação: app.config.errorHandler = (err, instance, info) => { ... }. Fica entre createApp() e app.mount(). O guia do Vue é explícito quanto à ordem: todas as configurações da aplicação devem ser aplicadas antes de a montar. A razão percebe-se bem aqui, porque a primeira renderização e o setup() do componente raiz são ambos executados na montagem.
A referência enumera sete origens das quais o gestor pode apanhar erros: renderizações de componentes, gestores de eventos, hooks de ciclo de vida, a função setup(), watchers, hooks de diretivas personalizadas e hooks de transição. Vale a pena ler essa lista como um contrato. Descreve o código que o Vue chama por si. O código que corre sem nenhuma chamada do Vue acima dele na pilha é outro assunto, e tem a sua própria secção mais abaixo.
Três argumentos e a armadilha da produção
O gestor recebe três argumentos. O primeiro é o erro, tipado como unknown na assinatura TypeScript, porque o JavaScript permite lançar qualquer coisa: uma string, um objeto simples, undefined. Verifique que se trata de uma instância de Error antes de ler a mensagem ou a pilha. O segundo é a instância do componente que desencadeou o erro, ou null. O terceiro, info, é uma string específica do Vue que identifica o tipo de origem, por exemplo em que hook de ciclo de vida o erro foi lançado.
- O app.config.errorHandler recebe (err, instance, info) e define-se antes de app.mount()
- Sete origens documentadas: renderizações de componentes, gestores de eventos, hooks de ciclo de vida, setup(), watchers, hooks de diretivas personalizadas, hooks de transição
- Nos builds de produção, info é um código curto; a página Production Error Code Reference devolve-o ao texto
- Comportamento predefinido: voltar a lançar em desenvolvimento, escrever na consola em produção; throwUnhandledErrorInProduction (3.5+) altera o segundo
- Um hook onErrorCaptured() que devolve false para o erro antes do gestor global
- Fora do Vue: o evento error de window para erros de script síncronos, unhandledrejection para as promises
Esse terceiro argumento reserva uma surpresa a quem abre um relatório de produção pela primeira vez. Nos builds de produção, info é um código abreviado em vez da string completa. A documentação do Vue mantém uma página Production Error Code Reference que faz corresponder cada código de execução ao seu texto original. Envie o valor em bruto tal como chega e faça a consulta ao ler o relatório. Uma lógica que compare info com uma string vista em desenvolvimento deixa de coincidir assim que a aplicação é compilada.
Desenvolvimento e produção não se comportam da mesma forma
Sem gestor, o que o Vue faz depende do build. Segundo a referência, o gestor predefinido volta a lançar os erros em desenvolvimento e regista-os em produção. A metade do desenvolvimento é deliberada. A documentação diz que o erro é lançado, podendo mesmo fazer a aplicação falhar, para ficar mais visível e assim ser notado e corrigido.
A metade da produção tem um efeito secundário que conta para a monitorização. Um erro que é apenas escrito na consola não é lançado, e a própria documentação nota que isso pode impedir os serviços de monitorização de erros de apanhar os que só acontecem em produção. Desde o Vue 3.5 existe um interruptor para esse caso: app.config.throwUnhandledErrorInProduction, um booleano cujo valor predefinido é false. Com true, os erros não tratados são lançados também em modo de produção.
onErrorCaptured: barreiras locais antes do gestor global
O gestor global é a última paragem. Antes dele, qualquer componente pode intercetar os erros que vêm dos seus descendentes com onErrorCaptured(), ou errorCaptured na Options API. O hook recebe os mesmos três argumentos e é a ferramenta para uma barreira de erros: alterar um estado, mostrar um conteúdo de recurso. A referência junta um aviso. O estado de erro não deve renderizar o conteúdo original que causou o erro; caso contrário, o componente entra num ciclo de renderização infinito.
A propagação segue quatro regras documentadas. Por omissão, os erros apanhados são na mesma enviados para o app.config.errorHandler, se estiver definido, para poderem ser reportados num único sítio. Se houver vários hooks errorCaptured na cadeia de pais, todos são invocados, de baixo para cima, de forma semelhante ao bubbling dos eventos do DOM. Se um hook lançar ele próprio um erro, tanto esse como o original seguem para o gestor global. E um hook pode devolver false para parar o erro ali: nenhum outro hook nem o gestor global serão invocados para ele. Esta última regra costuma ser a explicação para um erro que mostra um conteúdo de recurso no ecrã e nunca aparece num relatório.
O que o gestor global nunca vê
Agora a parte que fica fora do contrato. Um callback passado a setTimeout, um listener adicionado à mão com addEventListener, uma cadeia de promises iniciada num módulo utilitário, um script de terceiros: nenhum deles é uma das sete origens. O browser tem dois eventos para eles. Segundo a MDN, o evento error é disparado em window quando um recurso não pôde ser carregado ou utilizado, por exemplo quando um script tem um erro de execução, e só é gerado para erros de script lançados de forma síncrona. Uma promise rejeitada sem gestor de rejeição, o que inclui um throw não apanhado dentro de uma função async, dispara em vez disso unhandledrejection.
Uma configuração completa tem, portanto, três escutas a alimentar uma única função: app.config.errorHandler para o código que o Vue chama, um listener de error em window para as falhas síncronas noutros pontos e um listener de unhandledrejection para as promises que ninguém apanhou. Se faltar um, toda uma classe de falhas fica invisível.
Reportar sem piorar as coisas
O que a função faz com um erro decide se o gestor ajuda. Mantenha-a pequena e defensiva: normalizar o erro, anexar info, a rota atual e a versão publicada, e depois enviá-lo para o seu endpoint. Envolva o corpo num try e num catch, porque uma função de reporte que falha só acrescenta uma segunda falha à primeira. Elimine duplicados antes de enviar, já que um erro de renderização dentro de uma lista pode repetir-se em cada linha. E mantenha uma chamada a console.error no gestor enquanto desenvolve: depois de instalar o seu, o gestor predefinido do Vue, o que volta a lançar o erro em desenvolvimento, deixa de ser executado.
Os erros respondem a uma única pergunta: o que é que se partiu. Não dizem quanto tempo o utilizador esperou antes da falha, nem que pedido estava em curso nesse momento. Esse é o território dos traços, tratado em OpenTelemetry numa app Vue. Comece, ainda assim, pelo gestor. São poucas linhas sem qualquer dependência, e ele reporta os problemas que não sabia que devia procurar.
FAQ
Onde se define o gestor global de erros no Vue 3?
Na instância da aplicação, entre createApp() e app.mount(): app.config.errorHandler = (err, instance, info) => { ... }. O guia do Vue pede que todas as configurações da aplicação sejam aplicadas antes de a montar.
O app.config.errorHandler apanha todos os erros da página?
Não. A referência do Vue enumera sete origens: renderizações de componentes, gestores de eventos, hooks de ciclo de vida, setup(), watchers, hooks de diretivas personalizadas e hooks de transição. Os erros que surgem fora delas são comunicados pelo browser, através do evento error de window para erros de script síncronos e através de unhandledrejection para promises rejeitadas sem gestor.
Porque é que o argumento info é um código e não uma string legível em produção?
Nos builds de produção, o Vue passa um código abreviado como terceiro argumento do app.config.errorHandler e do onErrorCaptured. A página Production Error Code Reference da documentação do Vue faz corresponder cada código de execução à sua string de informação original.
Qual é a diferença entre onErrorCaptured e app.config.errorHandler?
O onErrorCaptured é um hook de componente que apanha erros dos componentes descendentes, o que faz dele a ferramenta para um conteúdo de recurso local. O app.config.errorHandler vale para toda a aplicação e recebe na mesma esses erros por omissão, a não ser que um hook devolva false para parar a propagação.



Uma configuração completa tem, portanto, três escutas a alimentar uma única função: app.config.errorHandler para o código que o Vue chama, um listener de error em window para as falhas síncronas noutros pontos e um listener de unhandledrejection para as promises que ninguém apanhou. Se faltar um, toda uma classe de falhas fica invisível.