Expected failures such as rejected requests belong in feature state with retry or fallback UI. Unexpected render, setup, watcher, lifecycle, directive, transition, and event errors flow through Vue error capture and finally the application handler.
Recovery and reporting are separate decisions: preserve a usable interface locally and send sanitized diagnostic context to one reporting boundary.
Classify the failure before choosing an API. A validation response, cancellation, offline request, and empty result are feature outcomes; a render exception or watcher defect belongs to Vue error capture.
| Failure | Owner | Response |
|---|---|---|
| Request or validation result | Feature composable or store | Loading, error, retry, or correction state. |
| Descendant render or lifecycle error | Nearest component boundary | Replace only the failed region. |
| Unhandled Vue application error | app.config.errorHandler | Report sanitized context. |
| Script failure outside Vue | Browser monitoring boundary | Record only application-owned code. |
fetch resolves for HTTP error statuses, so check response.ok. Treat AbortError as cancellation, cancel obsolete requests, and prevent a stale response from replacing newer state.
const data = ref(null);
const error = ref("");
const loading = ref(false);
let controller: AbortController | undefined;
async function load() {
controller?.abort();
controller = new AbortController();
loading.value = true;
error.value = "";
try {
const response = await fetch("/api/profile", {
signal: controller.signal
});
if (!response.ok) {
throw new Error("Request failed: " + response.status);
}
data.value = await response.json();
} catch (cause) {
if (cause instanceof DOMException &&
cause.name === "AbortError") return;
error.value = "Profile could not be loaded.";
} finally {
loading.value = false;
}
}
onBeforeUnmount(() => controller?.abort());
The feature owns expected request state and never renders raw server details.
onErrorCaptured observes errors from descendants and receives the error, component instance, and source information. Returning false stops propagation, so use it only when the boundary renders a deliberate fallback and records the failure.
Retrying must recreate the failed child or provide different input; merely hiding the fallback does not repair a broken instance.
<script setup>
import { onErrorCaptured, ref } from "vue";
const failed = ref(false);
const retryKey = ref(0);
onErrorCaptured((error, instance, info) => {
failed.value = true;
reportError({ error, info });
return false;
});
function retry() {
failed.value = false;
retryKey.value += 1;
}
</script>
<template>
<section v-if="failed" role="alert">
<p>This widget could not be displayed.</p>
<button @click="retry">Try again</button>
</section>
<slot v-else :key="retryKey" />
</template>
The boundary preserves the rest of the page and deliberately stops propagation after reporting.
app.config.errorHandler receives uncaught application errors, a component instance when available, and Vue source information. Production may provide a shortened source code, so store the release and Vue version with each event.
Do not throw from the reporter or expose props, tokens, request bodies, or personal data. Deduplicate repeated failures and keep user messaging at the owning feature boundary.
app.config.errorHandler = (error, instance, info) => {
try {
reportError({
message: error instanceof Error
? error.message
: "Unknown Vue error",
component: instance?.$options.name ?? "anonymous",
source: info,
release: import.meta.env.VITE_RELEASE
});
} catch {
console.error("Error reporting failed");
}
};
The handler reports unexpected defects; it does not replace local loading and retry state.
An async component loader can fail because a chunk is missing, a deployment replaced old assets, or the network is unavailable. Define loading, error, delay, timeout, and a controlled retry policy with defineAsyncComponent.
Suspense coordinates pending async dependencies but does not provide its own error handling. Capture rejected async setup from an ancestor boundary.
Try this next
0 of 2 completed
It captures descendant errors, so place the boundary above the component that may fail.
Suspense coordinates pending state but does not provide its own error handling.
Vue warnings are development-only. Fix them during development instead of treating them as production reporting.
Explore 500+ free tutorials across 20+ languages and frameworks.