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.
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.