Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

RFC: unstable_rethrow API #64076

balazsorban44 announced in RFC
Discussion options

Co-authored by: @wyattjoh

Goals

  • Make it easier for developers to write error-safe code that utilizes try/catch blocks around Next.js APIs that rely on throwing
  • Avoid developers having to reach for internals to solve this issue, or compare against magic strings

Background

The following Next.js APIs rely on throwing an error which should be rethrown and handled by Next.js itself.:

Next.js throws errors to communicate navigation intent to interrupt the code execution and React component rendering. These errors propagate up to the browser when streaming to allow the browser to perform the action, change the status code and/or return a different page when running on the server.

If a route segment is marked to throw an error unless it's static, a dynamic function call will also throw an error that should similarly not be caught by the developer. Note that Partial Prerendering (PPR) affects this behavior as well. These APIs are:

Existing issues

Developers are currently asked to call these APIs outside try/catch blocks, but this is easy to miss or not always ergonomic:

Proposal

Next.js will provide a new API, that will work both on the client and server: function unstable_rethrow(error: unknown): void | never, which handles all Next.js errors by re-throwing them to allow Next.js to take over the error handling, and continue execution for all other cases so the developer can provide their own error-handling mechanism.

This method should be called at the top of the catch block, passing the error object as its only argument. It can also be used within a .catch handler of a promise.

Additionally, if you defined your own [ErrorBoundary component](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary), unstable_rethrow should also be called at the top of the getDerivedStateFromError and componentDidCatch methods. Note, if you used the Next.js error.js file, Next.js will re-throw those errors for you.

It's important to note though that these navigation APIs (like notFound() or redirect()) will always throw when called, this tool is useful but you may not need it. If you ensure that your calls to API's that throw are not wrapped in a try/catch then you shouldn't required unstable_rethrow.

It's important to know that unstable_rethrow, if passed an internal Next.js errror, it will throw. This means that any resource cleanup (like clearing intervals, timers, etc) would have to either happen prior to the call to unstable_rethrow or within a finally block.

Example usage:

Example using a try/catch block:

import { unstable_rethrow as rethrow } from 'next'
import { createOrder } from '@/lib'
import { redirect } from 'next/navigation'
export default function Page() {
 return (
 <form
 action={async (formData: FormData) => {
 'use server'
 try {
 const res = await createOrder(formData)
 redirect(res.url)
 } catch (error) {
 rethrow(error) // This handles Next.js errors
 // Handle non-Next.js errors
 }
 }}
 >
 {/* ... */}
 </form>
 )
}

Or using a promise's .catch handler:

import { unstable_rethrow as rethrow } from 'next'
import { login } from '@/lib'
import { redirect } from 'next/navigation'
export default function Page() {
 return (
 <form
 action={async (formData: FormData) => {
 'use server'
 
 await login(formData).catch((error) => {
	 rethrow(error) // This handles Next.js errors
 // Handle non-Next.js errors
 })
 }}
 >
 {/* ... */}
 </form>
 )
}

Caveats

The unstable_rethrow API will not inspect error objects that have their source errors nested. If a library calls redirect() within a try/catch block, but then nests the error like:

async function api() {
	try {
	 // ...
	 redirect(...)
	} catch (cause) {
	 // should have called `unstable_rethrow(cause)` here!
	 throw new Error("Something bad happened!", { cause })
	}
}
async function Component() {
 try {
 await api()
 } catch (err) {
 // won't work
 unstable_rethrow(err)
 
 // will work, better to avoid nesting or calling `unstable_rethrow` whenever
 // a `try/catch` block is used.
 unstable_rethrow(err.cause)
 }
 
 // ...
}

Alternatives

  1. Do nothing - expose API's for users to inspect the errors themselves. This results in more custom code.
  2. Don't use throwing in Next.js API's - without throwing, we have no way to inurrupt the Next.js/React render pipeline, difficult to mark and track usage of dynamic data.
You must be logged in to vote

Replies: 3 comments 1 reply

Comment options

I agree in the importance of this API in addition to checking against both error and error.cause by default.

You must be logged in to vote
0 replies
Comment options

[...] without throwing, we have no way to inurrupt the Next.js/React render pipeline, difficult to mark and track usage of dynamic data.

I find this to be a reasonable solution if redesigning the public APIs to not throw errors is infeasible. I think it is intuitive that dynamic functions (cookies() et al) throw when a segment is set to dynamic = 'error', as you're explicitly opting into this behavior. However, I think what makes this pattern unintuitive cannot be fixed with an escape hatch such as this proposal.

async function action() {
 try {
 ...
 // This just feels like a dangling promise and a missing `return` before `redirect()`
 redirect(...)
 } catch(err) {
 ...
 }
}

The proposed unstable_rethrow(err) is not dissimilar to the redirect family in its uncomfortable ability to opaquely disrupt control flow unless you know what it is rethrowing. Accidentally bailing before cleanup as mentioned seems like another easy footgun and documentation that needs to be written when prior APIs let you just return <redirect method>. If this loss of readability and explicitness is a byproduct of the render pipeline as it is today, this is understandable, but otherwise throwing things that aren't actual errors is going to confuse developers and I'd rather try to avoid try/catch around these methods altogether.

You must be logged in to vote
0 replies
Comment options

in the server side gives me error while call unstable_rethrow ```
(0 , next_navigation__WEBPACK_IMPORTED_MODULE_5__.unstable_rethrow) is not a function

You must be logged in to vote
1 reply
Comment options

What is the next version in your package.json?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

AltStyle によって変換されたページ (->オリジナル) /