nuxt icon indicating copy to clipboard operation
nuxt copied to clipboard

Bug: Handling errors with the NuxtErrorBoundary component

Open lautiamkok opened this issue 1 year ago • 20 comments

The NuxtErrorBoundary component does not seem to work as expected:

The #error slot will receive error as a prop. (If you set error = null it will trigger re-rendering the default slot;

Example:

// app.vue
<template>
  <NuxtErrorBoundary>
    <NuxtPage/>
    <template #error="{ error }">
      <p>An error occurred: {{ error }}</p>
      <button @click="() => clearError({ redirect: '/' })">
        This will clear the error.
      </button>
    </template>
  </NuxtErrorBoundary>
</template>

Problem 1:

The error is NOT cleared at all when the button is clicked. The error message still can be seen on the screen.

Problem 2:

The Home page is NOT rendered at all when the route is redirected to / successfully. It is still rendering the error component and message.

Any ideas?

lautiamkok avatar Nov 25 '22 09:11 lautiamkok

Hey! Could you kindly provide a reproduction via StackBlitz or CodeSandbox? 🙏

manniL avatar Nov 25 '22 15:11 manniL

@manniL here it is:

https://stackblitz.com/edit/github-qsnf12?file=app.vue

the NuxtErrorBoundary component does not work on StackBlitz at all. So you need to download the sample app and run it locally then you see the problems.

Thanks

lautiamkok avatar Dec 03 '22 08:12 lautiamkok

Thanks!

Does it work fine with using it inside a page (so not app.vue)?

manniL avatar Dec 03 '22 12:12 manniL

@manniL nope. if you put that error component on /pages/about.vue, you will get the following error on your Console:

[vue-router warn]: uncaught error during route navigation:
[vue-router.js?v=414e8b35:2490 TypeError: num.toUpperCase is not a function

lautiamkok avatar Dec 04 '22 05:12 lautiamkok

Trying to use NuxtErrorBoundary on a component/page level, and I dont think its preventing global error page from rendering. I'm throwing createError from API and trying to gracefully show it while not rendering the global error page. Hopefully this ifxes soon... I'm about to pull all my hair out.

sangyoo91 avatar Dec 15 '22 15:12 sangyoo91

Yep, Even though I use NuxtErrorBoundary the full error page shows up.

metkm avatar Dec 23 '22 18:12 metkm

Hi I have the same issue using NuxtErrorBoundary around NuxtPage 👍

    <NuxtErrorBoundary>
        <NuxtPage />
    </NuxtErrorBoundary>

Any idea or workaround ?

CharlesBT avatar Jan 17 '23 15:01 CharlesBT

Hey, work for me in app.vue image

YourItalianChef avatar Feb 06 '23 15:02 YourItalianChef

Any update about this issue ? When NuxtErrorBoundary is used the full error page shows up.

BartoszKubica avatar Mar 01 '23 20:03 BartoszKubica

With NuxtErrorBoundary in app.vue, ssr: false, and an error thrown from a page component, nothing appears at all, just an error in the console. The error will show properly on page navigation, but not on initial page load.

[Vue warn]
[Vue warn]: Unhandled error during execution of setup function 
  at <Index onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > > 
  at <RouteProvider key="/projects/817c4d302bdaae79da0a6778" routeProps= 
Object { Component: {…}, route: {…} }
 pageKey="/projects/817c4d302bdaae79da0a6778"  ... > 
  at <BaseTransition onAfterLeave= 
Array [ onAfterLeave() ]
 mode="out-in" appear=false  ... > 
  at <Transition onAfterLeave= 
Array [ onAfterLeave() ]
 name="page" mode="out-in" > 
  at <RouterView name=undefined route=undefined > 
  at <NuxtPage> 
  at <[projectId] onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< 
Proxy { <target>: Proxy, <handler>: {…} }
 > > 
  at <RouteProvider key="/projects/817c4d302bdaae79da0a6778" routeProps= 
Object { Component: {…}, route: {…} }
 pageKey="/projects/817c4d302bdaae79da0a6778"  ... > 
  at <BaseTransition onAfterLeave= 
Array [ onAfterLeave() ]
 mode="out-in" appear=false  ... > 
  at <Transition onAfterLeave= 
Array [ onAfterLeave() ]
 name="page" mode="out-in" > 
  at <RouterView name=undefined route=undefined > 
  at <NuxtPage> 
  at <Anonymous onError=fn<logError> > 
  at <App key=2 > 
  at <NuxtRoot> chunk-3NMN3MUW.js:1381

Error
Uncaught (in promise) Error: Project not found
    NuxtJS 3
    setup index.vue:20
    callWithErrorHandling chunk-3NMN3MUW.js:1580
    setupStatefulComponent chunk-3NMN3MUW.js:7383
    setupComponent chunk-3NMN3MUW.js:7346
    mountComponent chunk-3NMN3MUW.js:6033
    processComponent chunk-3NMN3MUW.js:6011
    patch chunk-3NMN3MUW.js:5700
    componentUpdateFn chunk-3NMN3MUW.js:6128
    run chunk-3NMN3MUW.js:405
    update chunk-3NMN3MUW.js:6220
    setupRenderEffect chunk-3NMN3MUW.js:6228
    mountComponent chunk-3NMN3MUW.js:6046
    processComponent chunk-3NMN3MUW.js:6011
    patch chunk-3NMN3MUW.js:5700
    mountSuspense chunk-3NMN3MUW.js:2417
    process chunk-3NMN3MUW.js:2397
    patch chunk-3NMN3MUW.js:5704
    componentUpdateFn chunk-3NMN3MUW.js:6128
    run chunk-3NMN3MUW.js:405
    update chunk-3NMN3MUW.js:6220
    setupRenderEffect chunk-3NMN3MUW.js:6228
    mountComponent chunk-3NMN3MUW.js:6046
    processComponent chunk-3NMN3MUW.js:6011
    patch chunk-3NMN3MUW.js:5700
    componentUpdateFn chunk-3NMN3MUW.js:6128
    run chunk-3NMN3MUW.js:405
    update chunk-3NMN3MUW.js:6220
    setupRenderEffect chunk-3NMN3MUW.js:6228
    mountComponent chunk-3NMN3MUW.js:6046
    processComponent chunk-3NMN3MUW.js:6011
    patch chunk-3NMN3MUW.js:5700
    componentUpdateFn chunk-3NMN3MUW.js:6128
    run chunk-3NMN3MUW.js:405
    update chunk-3NMN3MUW.js:6220
    setupRenderEffect chunk-3NMN3MUW.js:6228
    mountComponent chunk-3NMN3MUW.js:6046
    processComponent chunk-3NMN3MUW.js:6011
    patch chunk-3NMN3MUW.js:5700
    componentUpdateFn chunk-3NMN3MUW.js:6128
    run chunk-3NMN3MUW.js:405
    update chunk-3NMN3MUW.js:6220
    setupRenderEffect chunk-3NMN3MUW.js:6228
    mountComponent chunk-3NMN3MUW.js:6046
    processComponent chunk-3NMN3MUW.js:6011
    patch chunk-3NMN3MUW.js:5700
    mountChildren chunk-3NMN3MUW.js:5845
    mountElement chunk-3NMN3MUW.js:5777
    processElement chunk-3NMN3MUW.js:5764
    patch chunk-3NMN3MUW.js:5698
    componentUpdateFn chunk-3NMN3MUW.js:6128
    run chunk-3NMN3MUW.js:405
    update chunk-3NMN3MUW.js:6220
    setupRenderEffect chunk-3NMN3MUW.js:6228
    registerDep chunk-3NMN3MUW.js:2696
    promise callback*registerDep chunk-3NMN3MUW.js:2682
    mountComponent chunk-3NMN3MUW.js:6039
    processComponent chunk-3NMN3MUW.js:6011
    patch chunk-3NMN3MUW.js:5700
    componentUpdateFn chunk-3NMN3MUW.js:6128
    run chunk-3NMN3MUW.js:405
    update chunk-3NMN3MUW.js:6220
    setupRenderEffect chunk-3NMN3MUW.js:6228
    mountComponent chunk-3NMN3MUW.js:6046
    processComponent chunk-3NMN3MUW.js:6011
    patch chunk-3NMN3MUW.js:5700
    mountSuspense chunk-3NMN3MUW.js:2417
    process chunk-3NMN3MUW.js:2397
    patch chunk-3NMN3MUW.js:5704
    componentUpdateFn chunk-3NMN3MUW.js:6128
    run chunk-3NMN3MUW.js:405
    update chunk-3NMN3MUW.js:6220
    setupRenderEffect chunk-3NMN3MUW.js:6228
    mountComponent chunk-3NMN3MUW.js:6046
    processComponent chunk-3NMN3MUW.js:6011
    patch chunk-3NMN3MUW.js:5700
    componentUpdateFn chunk-3NMN3MUW.js:6128
    run chunk-3NMN3MUW.js:405
    update chunk-3NMN3MUW.js:6220
    setupRenderEffect chunk-3NMN3MUW.js:6228
    mountComponent chunk-3NMN3MUW.js:6046
    processComponent chunk-3NMN3MUW.js:6011
    patch chunk-3NMN3MUW.js:5700
    componentUpdateFn chunk-3NMN3MUW.js:6128
    run chunk-3NMN3MUW.js:405
index.mjs:100:5

CallumAtCarter avatar Mar 13 '23 12:03 CallumAtCarter

This component doesn't seem to have anything

wf-soft avatar Apr 01 '23 10:04 wf-soft

FWIW, I'm using a home-grown components/ErrorBoundary.vue:

<script setup lang="ts">
const error = ref<Error>()

function clearError() {
  error.value = undefined
}

onErrorCaptured(err => {
  error.value = err
  return false
})

const route = useRoute()
watch(
  () => route.fullPath,
  () => {
    error.value = undefined
  },
)
</script>

<template>
  <slot v-if="!error" />
  <slot v-else name="error" :error="error" :clear-error="clearError" />
</template>

Features:

  • does what NuxtErrorBoundary advertises
  • works with async components
  • resets automatically on route change
  • can be used on parent.vue alongside parent/child1.vue and parent/child2.vue (handles crash in child1 and allows to switch to child2)

This is how one can use it with nuxt-page:

<error-boundary>
  <nuxt-page v-bind="$attrs" />
  <template #error="{ error, clearError }">
    <p>{{ error }}</p>
    <button @click="clearError">Try again</button>
  </template>
</error-boundary>

IlyaSemenov avatar Jun 15 '23 09:06 IlyaSemenov

Thanks @IlyaSemenov. Nuxt source clearly doesn't do what it advertises. There's no route watching in there to clear errors like your homegrown version does.

However, even with your solution, I have to click a NuxtLink twice to get the route watcher to actually fire. For some reason the first route change does not register with the watcher even though I see the route change in address bar. Certainly nothing wrong with your example. It's either me, or some quirk of route/watcher.

mvandiest avatar Jul 07 '23 20:07 mvandiest

Same issue here. When navigating to another page via any means (NuxtLink, navigateTo, clearError with redirect etc), the error persists. The docs say:

If you navigate to another route, the error will be cleared automatically.

But i'm not seeing this behavior.

The only thing that reliably clears the error is setting the error ref passed to the slot to undefined/null:

  <NuxtErrorBoundary>
    <template #error="{ error }">
      <button @click="error.value = null">clear error</button>
    </template>
    <!-- ... -->
  </NuxtErrorBoundary>

but this is far from ideal.

@IlyaSemenov's solution is an improvement, but as @mvandiest describes it requires two clicks on a NuxtLink.

WickyNilliams avatar Nov 13 '23 12:11 WickyNilliams

OK, i've done a little more testing. If i put the NuxtErrorBoundary within a page, the error does get cleared on route change. My issue (perhaps everyone else's too?) seems to be related to the error boundary being placed in a layout.

WickyNilliams avatar Nov 13 '23 12:11 WickyNilliams

i tried clearError function at many different places but it worked NEVER! Apart from that useError always returns undefined

awacode21 avatar Jan 24 '24 16:01 awacode21

Here is my own take on an ErrorBoundary component:

<script lang="ts">
let handlerSetup = false
const handlers: Array<() => void> = []
</script>

<script lang="ts" setup>
const error = shallowRef<any>(null)

onErrorCaptured((err) => {
  // eslint-disable-next-line no-console
  console.log('error', err)
  error.value = err
  return false
})

const router = useRouter()

async function clearError(options: { redirect?: any } = {}) {
  if (options.redirect) {
    await router.push(options.redirect)
  }
  error.value = null
}

if (!handlerSetup) {
  router.beforeEach(() => {
    handlers.forEach(handler => handler())
  })
  handlerSetup = true
}

handlers.push(clearError)

onBeforeUnmount(() => {
  const index = handlers.indexOf(clearError)
  if (index !== -1) {
    handlers.splice(index, 1)
  }
})
</script>

<template>
  <slot
    v-if="!error"
  />

  <slot
    v-else
    name="error"
    :error="error"
    :clear-error="clearError"
  />
</template>

Akryum avatar Feb 08 '24 10:02 Akryum

Just tried our the NuxtErrorBoundary component and it didn't work 🥲.

<template>
	<div class="flex h-full">
		<div class="flex h-full w-[270px] flex-col gap-1 rounded-lg p-2 py-4">
			<m-left-nav-menu :links="links"> </m-left-nav-menu>
		</div>
		<div class="flex grow flex-col gap-4 p-4 lg:p-8">
			<m-error-boundary>
				<nuxt-page />
				<template #error="{ error }">
					<m-error :error />
				</template>
			</m-error-boundary>
		</div>
	</div>
</template>

When I use the NuxtErrorBoundary component my m-error component does not work well at all. The contents of the error like {{error.statusCode}} don't show up.

Everything works as expected with @Akryum's ErrorBoundary component. But, I am building with ssr:false I see a flash of the page before the error comes up 🥲

tobychidi avatar Mar 18 '24 08:03 tobychidi

Also when I encounter an error and the error boundary is shown, a page refresh would cause a blank page.

image

tobychidi avatar Mar 18 '24 12:03 tobychidi

FWIW, I'm using a home-grown components/ErrorBoundary.vue:

<script setup lang="ts">
const error = ref<Error>()

function clearError() {
  error.value = undefined
}

onErrorCaptured(err => {
  error.value = err
  return false
})

const route = useRoute()
watch(
  () => route.fullPath,
  () => {
    error.value = undefined
  },
)
</script>

<template>
  <slot v-if="!error" />
  <slot v-else name="error" :error="error" :clear-error="clearError" />
</template>

Features:

  • does what NuxtErrorBoundary advertises
  • works with async components
  • resets automatically on route change
  • can be used on parent.vue alongside parent/child1.vue and parent/child2.vue (handles crash in child1 and allows to switch to child2)

This is how one can use it with nuxt-page:

<error-boundary>
  <nuxt-page v-bind="$attrs" />
  <template #error="{ error, clearError }">
    <p>{{ error }}</p>
    <button @click="clearError">Try again</button>
  </template>
</error-boundary>

hi. is this solution works on ssr?

Edit: Yes it works perfect both in csr and ssr mode. Why this solution is not in the documents and everywhere??

bayramorhan avatar Apr 26 '24 09:04 bayramorhan