🛫

Next.js(App Router)のredirect関数に生じる謎のエラー

に公開

現象

App Router で新たに追加されたredirect 関数は、server actions やサーバーコンポーネント、route handlers で使うことができますが、実行時に、

Error: NEXT_REDIRECT at…

のようなエラーに出くわすことがあります。割と遭遇して日も経ちますが、備忘録として残しておきます。

結論

まずは結論です。以下の場合、上記のエラーが発生します。

  • try...catch 構文の中で redirect 関数を使っている場合
  • Promise を返却する関数内で使用する場合

解決方法は簡単で、

  • これらの外で redirect 関数を使う。
  • そもそも redirect 関数を使わずリダイレクト処理をする。

後者はそもそもなので、前者だけ例を示します。

example.ts
export const sendVerificationEmail = async (formData: FormData) => {
  "use server";

  try {
    const formJson = formDataToJSON(formData);
    const result = SIGN_IN_SCHEMA.safeParse(formJson);
    if (!result.success) {
      // zodでvalidationに引っかかった時に行われる処理
      ...
      redirect("/sign-in");
    } else {
      const {
        data: { email },
      } = result;

      // validationに通った時に行われる処理
      ...

      redirect("/");
    }
  } catch (err: any) {
   // catchの処理
  }
};

上記のように、server actions の try...catch の中に redirect 関数を使った場合、先述のエラーが発生します。この場合は、以下のように、try...catch 構文の外であ redirect 関数を実行します。

example.ts
export const sendVerificationEmail = async (formData: FormData) => {
  "use server";

  let redirectTo = "" // リダイレクト先

  try {
    const formJson = formDataToJSON(formData);
    const result = SIGN_IN_SCHEMA.safeParse(formJson);
    if (!result.success) {
      // zodでvalidationに引っかかった時に行われる処理
      ...
      redirectTo = "/sign-in";
    } else {
      const {
        data: { email },
      } = result;

      // validationに通った時に行われる処理
      ...

      redirectTo = "/";
    }
  } catch (err: any) {
   // catchの処理
  }

  // try...catchの外でredirect関数を実行する。
  if (redirectTo !== "") {
    redirect(redirectTo)
  }
};

これでエラーの発生は避けられます。

原因

redirect 関数の中身を node_modules から覗いてみると、以下のようになっています。

redirect.js
function redirect(url, type) {
    if (type === void 0) type = "replace";
    const actionStore = _actionasyncstorageexternal.actionAsyncStorage.getStore();
    throw getRedirectError(url, type, // If we're in an action, we want to use a 303 redirect
    // as we don't want the POST request to follow the redirect,
    // as it could result in erroneous re-submissions.
    (actionStore == null ? void 0 : actionStore.isAction) ? _redirectstatuscode.RedirectStatusCode.SeeOther : _redirectstatuscode.RedirectStatusCode.TemporaryRedirect);
}

上記のように、内部で throw をしエラーを発生させているので、try...catch の中や非同期関数の中で使用するとエラーが発生してしまいます。

references

プラスクラス・スポーツ・インキュベーション株式会社

Discussion