Error Handling

onErrorCaptured & app.config.errorHandler

Error Handling

Vue has two layers of error handling — onErrorCaptured for local boundaries and app.config.errorHandler for the global catch-all.

4 min read Level 3/5 #vue#errors#boundaries
What you'll learn
  • Use onErrorCaptured to catch descendant errors
  • Return false to stop error propagation
  • Set a global app.config.errorHandler

Unlike React, Vue doesn’t need a special class component to catch errors. Any component can call onErrorCaptured and become an error boundary; the app has a global handler for everything else.

Component-Level Boundary

onErrorCaptured((err, instance, info)) => boolean | void runs when a descendant throws — in templates, lifecycle, watchers, render functions, or event handlers.

<script setup lang="ts">
import { onErrorCaptured, ref } from 'vue'

const error = ref<Error | null>(null)

onErrorCaptured((err, _instance, info) => {
  console.warn('caught', info, err)
  error.value = err as Error
  return false // stop propagation
})
</script>

<template>
  <div v-if="error">Something broke: {{ error.message }}</div>
  <slot v-else />
</template>

Returning false halts the bubble — the error won’t reach parent boundaries or the global handler.

Global Handler

Set app.config.errorHandler in main.ts to ship uncaught errors to Sentry, Datadog, or your own endpoint.

import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)
app.config.errorHandler = (err, instance, info) => {
  reportToSentry(err, { info })
}
app.mount('#app')

Async Errors

Promises rejected in setup propagate through <Suspense> — listen to its @fallback event or wrap the async work in try/catch to handle gracefully.

What’s Not Caught

onErrorCaptured doesn’t see plain setTimeout or unhandled promise rejections outside Vue’s lifecycle. Use window.addEventListener('error', …) for those.

Teleport →