skip to content

Error Capture & Handlers

onErrorCaptured catches errors from descendants and can return false to stop them; app.config.errorHandler is the last stop. Interviewers probe which errors each one actually sees.

part ofVue.jsoverview, primer and where to startread it →
on this pageshow

explore

questions

4

In Vue 3, what does `app.config.errorHandler` receive, which errors reach it, and what does Vue do with an error when no handler is set?

level: juniorimportance: should knowfreq 42%

answer

  1. the end of the propagation chain
  2. error, instance, info
  3. info shrinks in production
  4. unset: rethrow in dev, log in prod

basics

~20 s

Vue 3's app.config.errorHandler receives (err, instance, info) for errors in renders, setup, hooks, watchers, event handlers and directive or transition hooks that no onErrorCaptured stopped. Without it, Vue rethrows in development and only logs in production.

solid answer

~40 s

`app.config.errorHandler = (err, instance, info) => {}` is the last stop for errors Vue catches while running your component code: renders, `setup()`, lifecycle hooks, watchers, `v-on` handlers, custom directive hooks and transition hooks — including rejected promises those functions return. `instance` is the public instance of the component where the error happened; `info` names the source, such as `'render function'` or `'mounted hook'` in development and a short error-reference code in production. It runs only if no `onErrorCaptured` hook up the parent chain returned `false`. Without a handler, Vue warns `Unhandled error during execution of …` and rethrows in development, and only `console.error`s in production. Once a handler is set, Vue logs nothing itself, so a reporting handler should also log.

code

ts · 16 lines
ts
import { createApp } from 'vue'
import App from './App.vue'
import { reportError } from './monitoring'

const app = createApp(App)

app.config.errorHandler = (err, instance, info) => {
  // Vue stops logging these errors once a handler is set
  console.error(err)
  reportError(err, {
    source: info, // 'render function', 'mounted hook'... or a code in production
    props: instance?.$props,
  })
}

app.mount('#app')

go deeper

for a junior

Know that app.config.errorHandler is the app-wide place to report errors and that it receives the error, the component instance and an info string.

for a middle

Explain which sources reach it, that returned promises count, and the default rethrow-in-dev versus log-in-prod behaviour.

for a senior

Make the handler safe in production: keep console output, attach component context, never throw from it, and translate production info codes.

for a principal

Decide what the app treats as fatal versus recoverable, and whether production should keep logging or fail loudly on unhandled errors.

## Where the handler sits Whenever Vue calls code you wrote — a component's render function, `setup()`, a lifecycle hook, a watcher, an event handler bound with `v-on` — it calls it through an internal error-handling wrapper. If that code throws, Vue walks the error up the **component parent chain**, offering it to each ancestor's `onErrorCaptured` hooks. If none of them returns `false`, the error arrives at **`app.config.errorHandler`**, the application-wide last stop. It is set once, on the app instance, before `mount()`. ## The three arguments | Argument | What it holds | |---|---| | `err` | the thrown value — usually an `Error`, but typed `unknown` because anything can be thrown | | `instance` | the public instance of the component in whose code the error occurred, or `null` | | `info` | the source: `'render function'`, `'setup function'`, `'mounted hook'`, `'watcher callback'`, `'native event handler'`, `'component event handler'` and so on | In **production builds** `info` is not the readable string: to keep the bundle small it is a short code, which the Vue docs' Production Error Code Reference maps back to the source. Reporting code should store it as-is and translate later. ## Which errors reach it The Vue API reference lists the sources the handler can capture: - component renders; - event handlers, both `v-on` listeners on elements and handlers for component events; - lifecycle hooks; - the `setup()` function; - watchers — getter, callback and cleanup; - custom directive hooks; - transition hooks. For hooks, handlers and watcher callbacks, Vue also attaches a rejection handler when the function **returns a promise**, so an `async` hook that throws after an `await` is reported like a synchronous throw. Code Vue never called — a `setTimeout` callback, a promise chain nobody returned, a listener attached with `addEventListener` — never passes through this path. ## What happens without a handler | Build | No `errorHandler` | With `errorHandler` | |---|---|---| | Development | warns `Unhandled error during execution of <source>`, then **rethrows** the error | calls your handler; Vue logs nothing | | Production | `console.error(err)` and carries on | calls your handler; Vue logs nothing | Rethrowing in development is deliberate: a crash is more noticeable than a log line. In production, Vue recovers to limit the damage to users — a component whose render threw is left rendering an empty placeholder while the rest of the page keeps working. Vue 3.5 added an app option that makes production throw as well, for teams that prefer a hard failure. ## Writing a good handler 1. **Log it.** Setting a handler switches off Vue's own console output for these errors, so a handler that only sends to a reporting endpoint makes errors invisible in the browser console. 2. **Report with context.** Send `info` and something identifying the component, because a stack trace through compiled render functions rarely points at the template line. 3. **Do not throw from it.** An error thrown inside the handler is treated as a new error with no component context: it bypasses your handler and falls through to Vue's default path — a warning and rethrow in development, `console.error` in production. 4. **Keep it cheap and synchronous.** It can run during a render; queue the network work instead of awaiting it. ## Caveats - The handler only sees errors that occur **in a component's context**. An error thrown with no component involved skips it and goes straight to Vue's default logging. - It sees an error **only once** per propagation, and never if a descendant-level `onErrorCaptured` returned `false`. - It is **one per app**. Two apps on one page need two handlers. ## `errorHandler` versus `onErrorCaptured` | | `app.config.errorHandler` | `onErrorCaptured` | |---|---|---| | Scope | the whole app | descendants of one component | | Registered | once, on the app, before mount | during a component's `setup()` | | Can stop propagation | it is the end of the chain | yes, by returning `false` | | Typical job | log and report centrally | show a local fallback, add context | Most apps want both: local hooks for user-facing recovery, one app handler so that nothing a local hook lets through goes unreported. The interview-ready summary: three arguments, the listed sources plus returned promises, runs last, silences Vue's default logging, and rethrow-in-dev versus log-in-prod when it is absent.

  • Why might errors disappear from the browser console after a team adds `app.config.errorHandler`?
    Vue's default logging — the dev warning plus rethrow, or the production `console.error` — happens only when no handler exists. With a handler set, Vue calls it and returns. If the handler only posts to a reporting endpoint, nothing is printed locally, and a failing endpoint loses the error entirely. Log inside the handler as well.
  • Why is `info` a short code rather than `'render function'` in production?
    Vue strips the source-name strings from production builds to save bytes and passes a compact error-reference code instead. The Vue docs' Production Error Code Reference maps each code back to its source. Store the raw value in your reports and translate it when reading them, rather than branching on the development strings in handler logic.

saying these in an interview costs you the question

  • Vue still prints its own warning after you set app.config.errorHandler.
  • The errorHandler catches every error thrown anywhere on the page.
  • An async lifecycle hook that rejects bypasses the errorHandler.
  • Without a handler, Vue swallows errors silently in development.
  • The info argument is the same readable string in production.
open as a page

In Vue 3, which errors can `onErrorCaptured` and `app.config.errorHandler` see, and which never reach them, like a throw inside a `setTimeout` callback?

level: middleimportance: should knowfreq 38%

basics

~20 s

Vue 3 routes only errors from code it calls itself: renders, setup, lifecycle hooks, watchers, v-on handlers, directive and transition hooks, plus promises those return. Timer callbacks, un-returned promises and addEventListener listeners never reach its handlers.

open as a page

In Vue 3, in what order do `onErrorCaptured` hooks and `app.config.errorHandler` run for a descendant's error, and what does returning `false` change?

level: middleimportance: should knowfreq 45%

basics

~20 s

In Vue 3 an error starts at the failing component's parent and climbs the parent chain, running every onErrorCaptured hook bottom to top, then app.config.errorHandler. A hook returning false marks it handled: later hooks and the app handler are skipped.

open as a page

A dashboard widget throws during render in Vue 3; how do you show a fallback for just that widget with `onErrorCaptured` while the rest of the page keeps working?

level: seniorimportance: should knowfreq 36%

basics

~20 s

In Vue 3, wrap each widget in a component whose onErrorCaptured stores the error in a ref and whose template then shows a fallback instead of the slot. Report the error, never re-render the failing content, and retry with a new key.

open as a page