Skip to content

Error handling

By default, all errors thrown in a loader are considered unexpected errors: they will abort the navigation, just like in a navigation guard. Because they abort the navigation, they will not appear in the error property of the loader. Instead, they will be intercepted by Vue Router's error handling with router.onError().

However, if the loader is not navigation-aware, the error cannot be intercepted by Vue Router and will be kept in the error property of the loader. This is the case for lazy loaders and reloading data.

Defining expected Errors

To be able to intercept errors in non-lazy loaders, we can specify a list of error classes that are considered expected errors. This allows blocking loader to not abort the navigation and instead keep the error in the error property of the loader and let the page locally display the error state.

ts
import { 
defineBasicLoader
} from 'unplugin-vue-router/data-loaders/basic'
// custom error class class
MyError
extends
Error
{
// override is only needed in TS override
name
= 'MyError' // Displays in logs instead of 'Error'
// defining a constructor is optional constructor(
message
: string) {
super(
message
)
} } export const
useUserData
=
defineBasicLoader
(
async (
to
) => {
throw new
MyError
('Something went wrong')
// ... }, {
errors
: [
MyError
],
} )

You can also specify expected errors globally for all loaders by providing the errors option to the DataLoaderPlugin.

ts
app
.
use
(
DataLoaderPlugin
, {
router
,
// checks with `instanceof MyError`
errors
: [
MyError
],
})

Then you need to opt-in in the loader by setting the errors option to true to keep the error in the error property of the loader.

ts
export const 
useUserData
=
defineBasicLoader
(
async (
to
) => {
throw new
Error
('Something went wrong')
// ... }, {
errors
: true,
} )
Why is errors: true needed?

One of the benefits of Data Loaders is that they ensure the data to be ready before the component is rendered. With expected errors, this is no longer true and data can be undefined:

ts
export const 
useDataWithErrors
=
defineBasicLoader
(
async (
to
) => {
// ... }, {
errors
: true,
} ) const {
data
} =
useDataWithErrors
()
data
.
value
// `data` can be `undefined`

Custom Error handling

If you need more control over the error handling, you can provide a function to the errors option. This option is available in both the DataLoaderPlugin and when defining a loader.

ts
app
.
use
(
DataLoaderPlugin
, {
router
,
errors
: (
error
) => {
// Convention for custom errors if (
error
instanceof
Error
&&
error
.
name
?.
startsWith
('My')) {
return true } return false // unexpected error }, })

Handling both, local and global errors

TODO: this hasn't been implemented yet

Error handling priority

When you use both, global and local error handling, the local error handling has a higher priority and will override the global error handling. This is how the local and global errors are checked:

  • if local errors is false: abort the navigation -> data is not undefined
  • if local errors is true: rely on the globally defined errors option -> data is possibly undefined
  • else: rely on the local errors option -> data is possibly undefined

TypeScript

You will notice that the type of error is Error | null even when you specify the errors option. This is because if we call the reload() method (meaning we are outside of a navigation), the error isn't discarded, it appears in the error property without being filtered by the errors option.

In practice, depending on how you handle the error, you will add a type guard inside the component responsible for displaying an error or directly in a v-if in the template.

template
<template>
  <!-- ... -->
  <p v-if="isMyError(error)">{{ error.message }}</p>
</template>

If you want to be even stricter, you can override the default Error type with unknown (or anything else) by augmenting the TypesConfig interface.

ts
// types-extension.d.ts
import 'unplugin-vue-router/data-loaders'
export {}
declare module 'unplugin-vue-router/data-loaders' {
  interface TypesConfig {
    Error: unknown
  }
}

Released under the MIT License.