next.js: Error: NEXT_REDIRECT while using server actions
Verify canary release
- I verified that the issue exists in the latest Next.js canary release
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 22.4.0: Mon Mar 6 21:01:02 PST 2023; root:xnu-8796.101.5~3/RELEASE_ARM64_T8112
Binaries:
Node: 19.2.0
npm: 9.6.2
Yarn: 1.22.19
pnpm: 8.4.0
Relevant packages:
next: 13.4.1-canary.2
eslint-config-next: 13.4.0
react: 18.2.0
react-dom: 18.2.0
Which area(s) of Next.js are affected? (leave empty if unsure)
App directory (appDir: true)
Link to the code that reproduces this issue
To Reproduce
If the server actions don’t use a try...catch
block, then there is no error. The form action works as expected. However, if there is a try...catch
block, then the error Error: NEXT_REDIRECT
shows up.
Note that I’m using Prisma and the data gets saved before calling redirect(
/admin/forums/${forum.id});
. The forum
object has the correct data.
Describe the Bug
The following code works as expected:
const handleSubmit = async (formData: FormData) => {
"use server";
const session = await getServerSession(authOptions);
if (!session) {
throw new Error("Unauthorized");
}
const forum = await prisma.forum.create({
data: {
title: String(formData.get("title")),
description: String(formData.get("description")),
creatorId: session.user.id,
},
});
redirect(`/admin/forums//${forum.id}`);
}
However, if I use a try...catch
block, then the following error showing up:
The code that throws the above error is as follows:
const handleSubmit = async (formData: FormData) => {
"use server";
try {
const session = await getServerSession(authOptions);
if (!session) {
throw new Error("Unauthorized");
}
const data = Object.fromEntries(formData.entries());
const { title, description } = forumCommentCreateSchema.parse(data);
const forum = await prisma.forum.create({
data: {
title,
description,
creatorId: session.user.id,
},
});
redirect(`/admin/forums/${forum.id}`);
} catch (error) {
if (error instanceof z.ZodError) {
throw new Error(JSON.stringify(error.issues));
}
throw new Error("Something went wrong.");
}
};
Expected Behavior
The redirect()
function should redirect to the correct URL instead of throwing an error.
Which browser are you using? (if relevant)
Chromium Engine Version 112.0.5615.137
How are you deploying your application? (if relevant)
Vercel
About this issue
- Original URL
- State: closed
- Created a year ago
- Reactions: 11
- Comments: 29 (5 by maintainers)
I noticed a behaviour that’s not really intuitive at first:
→ The
redirect()
only works if my server action call is returned in thestartTransition()
callback.Otherwise said:
This works
This breaks with a
NEXT_REDIRECT
error in the console, and no redirection happening.I believe this is the source for your error @JoseVSeb.
Is this behaviour expected @timneutkens ?
As others have pointed out the way
redirect()
andnotFound()
work is by throwing an error that Next.js can handle in order to stop execution of the code.One way to fix it:
We’ll make sure the docs are updated to explain
redirect()
andnotFound()
use error throwing under the hood.Assuming
updateRecipientInfo
is a server action, the code provided is a dangling promise, whereas when youreturn
it’s an awaited promise.The right way to go about it is this:
In the experimental version of React that is enabled when you enable server actions async transitions are supported (it’s what
action
uses under the hood too.Hope that clarifies why it would not be caught when you “just call it” instead of returning. It’s similar to not awaiting any other async function
I ran into the same issue and this fixes it, but it throws a type error. A transition function can only return void or undefined, and returning a Promise<void> breaks that.
Thanks a lot for your answer 🙂
I didn’t realize an error in a dangling promise couldn’t be caught, and by reflex I used this the same way the nextjs documentation uses
router.refresh()
instartTransition
, although that’s not the same thing at all.Maybe an uncaught
NEXT_REDIRECT
would benefit from a more helpful error message? (I believe lots of newcomers will get bitten by that error).You have to place the
redirect()
outsidetry-catch
. Internally,redirect()
is throwing an Error object (see https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/redirect.ts#L12-L36), which is also why it’s typed asfunction redirect(...): never
.Try to place less code in a try-catch anyway. You’ll get the same behaviour of
throw new Error("Unauthorized");
.If you have a global error handler/wrapper, you can rethrow it with something like:
This sounds like something an eslint rule should be able to handle well
Hey folks,
I’m encountering the same issue with the latest release 13.4.3 something as simple as this:
Gives me the same redirect error as the OP, what am i missing?
I removed the try-catch from my code and now the redirect works, thanks
This is actually not the case when you enable server actions. The React experimental channel has support for Async Transitions, so an async function is supported in that case.
this is my server-side code:
I have confirmed that the
x-action-redirect
header is in the response with the correct redirect path/workflows/admission/d65a2be3-bf7d-4514-8606-8815295767a8
. redirection fails in client side.I removed try catch and works idk why but its works
Can you explain what the purpose of
useTransition
is here? I’ve seen it used with server actions a couple times but I don’t get the point!@benjaminwaterlot thanks, that was the issue. Although, i don’t understand why that’s needed.
useTransition
hook merely provides a pending state for promise resolution. Is server action using the hook to somehow detect redirection?You can use transition hook to wrapped the server action, it looks like this: