next.js: [NEXT-435] Minimal middleware (only NextResponse.next()), generates error on error pages in dev mode

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System: Platform: win32 Arch: x64 Version: Windows 10 Pro Binaries: Node: 16.17.0 npm: N/A Yarn: N/A pnpm: N/A Relevant packages: next: 13.1.0 eslint-config-next: N/A react: 18.2.0 react-dom: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

Routing (next/router, next/navigation, next/link)

Link to the code that reproduces this issue

https://github.com/w4zZz4p/issue-nextjs-error-page

To Reproduce

npm install
npm run dev

open http://localhost:3000/page-does-not-exist

Describe the Bug

When there is empty global middleware created:

import { NextResponse } from 'next/server';

export function middleware(request) {
    // some rewrite logic for specific requests

    // all other requests pass-trough
    return NextResponse.next();
}

and when you visit any 404 page, error occurs:

Unhandled Runtime Error
Error: Invariant: attempted to hard navigate to the same URL /page-does-not-exist http://localhost:3000/page-does-not-exist

The issue appeared in version v13.0.7-canary.5. Version v13.0.7-canary.4 works as expected. My assumption its related to https://github.com/vercel/next.js/pull/43836

Expected Behavior

There should be no error as in any existing page for example http://localhost:3000/

Which browser are you using? (if relevant)

Chrome 108.0.5359.125 (Official Build) (64-bit)

How are you deploying your application? (if relevant)

No response

NEXT-435

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 45
  • Comments: 24 (5 by maintainers)

Commits related to this issue

Most upvoted comments

Same issue here from v13.0.7 to latest (v13.1.1). Any Middleware file even if just export function middleware() {} breaks 404 page.

Adding the 404 page to exclude from the matcher does not work indeed.

As I commented before, it is not just on dev. A build is broken as well.

If you have a dynamic page with getStaticProps and getStaticPaths the 404 page will not get loaded. Even if you return { notFound: true } from the getStaticProps it will not work. After a refresh it sometimes does.

If you remove the middleware.js file (or just rename it for testing sake) it will work as expected again.

Reason for mentioning it is that I think some things around middleware and navigating are not going well. Not just on dev but also in a production environment.

@damien can you look at this issue? Seems more apps have encountered the issue

A poor workaround that worked for me is to just fallback rewrite to a custom error page and remove 404.tsx. The only con here is that it won’t respond with 404, but with 200 instead.

module.exports = {
  // ...
  rewrites() {
    return {
      fallback: [
        {
          source: "/:path*",
          destination: "/not-found",
          locale: false,
        },
      ],
    };
  }
};

EDIT: To make it respond with 404, add getServerSideProps() to the custom error page and make it return { notFound: true }


Complete workaround

next.config.js

module.exports = {
  // ...
  rewrites() {
    return {
      fallback: [
        // last entry
        {
          source: "/:path*",
          destination: "/404bug?path=:path*",
          locale: false,
        },
      ],
    };
  },
};

middleware.js

// The contents of this file actually doesn't matter. 
// It just needs to exist and be a valid middleware to trigger the bug.
import { NextResponse } from 'next/server';
export function middleware(request) {
    return NextResponse.next();
}

pages/404.jsx

export default () => <div>404 works</div>;

pages/404bug.jsx

export default () => null;
export const getServerSideProps = () => ({ notFound: true });

Seems to have been introduced at https://github.com/vercel/next.js/pull/43836

Namely, this code was removed.

      // We don't update for 404 requests as this can modify
      // the asPath unexpectedly e.g. adding basePath when
      // it wasn't originally present
      initialData.page !== '/404' &&
      initialData.page !== '/_error' &&

It seems like that when you have a middleware file, it triggers internal routing handling more than once. I noticed when running nextjs from source the first run is “good” but the second triggers this behaviour. This will also happen in production builds obviously.

I am not good enough into the nextjs repo to know how to fix or say something more educated about it, but that is what I noticed. It might help 🙈 .