Skip to content

Stale if Error ​

All caches support stale if error behaviour. This allows them to return expired stale cache items if an error happens during revalidation.

Data Cache ​

The useDataCache composable returns a staleValue if:

  • The data was cached by providing the staleIfError argument in addToCache
  • The cached data is still within the staleIfError range
typescript
async function loadUser(userId: string): Promise<User> {
  const { value, addToCache, staleValue } = await useDataCache<User>(
    'user:' + userId,
  )

  // A valid, non-stale value exists in the cache.
  if (value) {
    return value
  }

  try {
    const response = await $fetch<User>('/api/get-user/' + userId)

    await addToCache(
      // The object we want to cache.
      response,
      // The cache tags.
      ['user:' + userId],
      // The max age for the cached data.
      '5m',
      // How long staleValue should be returned.
      '2h',
    )
    return response
  } catch (e) {
    // Return a stale value if available. The composable only returns
    // staleValue if it's still within the specificed max stale-if-error range.
    if (staleValue) {
      return staleValue
    }

    // Re-throw the error so it can be handled properly.
    throw e
  }
}

Component Cache ​

The <RenderCacheable> component will return stale markup if an error happens during rendering of its default slot.

The stale if error duration can be set via the stale-if-error prop on the component:

vue
<template>
  <div>
    <RenderCacheable max-age="5m" stale-if-error="2h">
      <Navbar />
    </RenderCacheable>
  </div>
</template>

Or anywhere inside components that are rendered inside <RenderCacheable> using the useComponentCache composable:

typescript
useComponentCache((helper) => {
  helper.setMaxAge('5m').setStaleIfError('2h')
})

Route Cache ​

Use the setStaleIfError method on the route cache helper provided by useRouteCache:

vue
<template>
  <div>
    This page is cached for 5 minutes or up to 2h when revalidation fails.
  </div>
</template>

<script lang="ts" setup>
useRouteCache((helper) => {
  helper.setMaxAge('5m').setStaleIfError('2h').setCacheable()
})
</script>

The stale route is only served for server errors: errors with a 5xx status code and unhandled errors. A client error, such as a 404 thrown because the content was deleted, is sent as usual.

The stale route is sent with its cached status code and body instead of the error response, with the same headers as a cache hit: the ones set before any handler ran (for example by a Nitro request hook) and the cached headers. Headers that the failed request has set are not sent, including cookies, for example a renewed session.

The module adds a Nitro error handler for this, which runs before the app's error handler (Nuxt's error page, or a custom nitro.errorHandler). The app's error handler is not called when a stale route is served. The error is still logged like Nitro logs errors, and reported to Nitro's error hook, where error monitoring receives it. With Nuxt's experimental.inlineErrorRendering, a page that fails to render responds with the error page instead of throwing, and the module replaces the error page with the stale route.

Only an error that becomes the response serves the stale route. Errors that are only reported while the request continues, such as with event.captureError() or by Nitro's cache when its storage fails, don't.

Only Use the Cache on Errors ​

To never serve an item from cache, but keep a stale copy for when an error happens, set a max age of 0 together with a stale if error duration. This works for all three caches.

typescript
useRouteCache((helper) => {
  helper.setMaxAge(0).setStaleIfError('2h').setCacheable()
})

The stale copy is served to any request that fails, so this is not suited for responses with data specific to the request. Mark those as uncacheable instead.

Released under the MIT License.