next.js
next.js copied to clipboard
Custom error.tsx page is not rendered for dynamic routes using generateStaticParams / ISR in app router. Pages router 500.tsx is rendered instead
Link to the code that reproduces this issue
https://github.com/bkrajewski94/next-isr-error-page-bug
To Reproduce
- Install dependencies with
npm install
- Build the application with
npm run build
- Start the application
npm start
- Navigate to
localhost:3000/test
Current vs. Expected behavior
If you use generateStaticParams
inside a dynamic route like [slug]/page.tsx
, none of the custom error.tsx/global-error.tsx
pages will be displayed in case of an error. What you'll see instead is the NextJS inbuilt error page
In the below example I used generateStaticParams to implement ISR (on request static generation):
// src/app/[slug]/page.tsx
async function getData() {
const res = await fetch("https://api.example.com/...");
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error("Failed to fetch data");
}
return res.json();
}
export default async function Page({ slug }: any) {
const data = await getData();
return (
<main>
<div>{slug}</div>
<div>{JSON.stringify(data)}</div>
</main>
);
}
// error.tsx is not rendered when:
export function generateStaticParams() {
return [];
}
// error.tsx is rendered correctly when instead of generateStaticParams I use:
// export const dynamic = "force-dynamic";
It only breaks if you build your application (works fine on devserver). As you can imagine it can be a big problem when you'd like to e.g. render a custom error page, or log error to an external system inside the error boundary.
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 23.0.0: Fri Sep 15 14:41:43 PDT 2023; root:xnu-10002.1.13~1/RELEASE_ARM64_T6000
Binaries:
Node: 20.10.0
npm: 10.2.3
Yarn: 1.22.21
pnpm: 7.1.0
Relevant Packages:
next: 14.1.1-canary.52 // Latest available version is detected (14.1.1-canary.52).
eslint-config-next: 14.1.0
react: 18.2.0
react-dom: 18.2.0
typescript: 5.3.3
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
App Router
Which stage(s) are affected? (Select all that apply)
next build (local), next start (local), Other (Deployed)
Additional context
No response
I stumbled across this today, too, but for the 404 pages.
// src/app/[[...slug]]/page.tsx
export function generateStaticParams() {
return ["home", "about", "career"].map((slug) => ({ slug: [slug] }));
}
export const dynamicParams = false;
export default function Page({ params }: any) {
const { slug } = params;
return <div>{slug}.</div>;
}
// src/app/[[...slug]]/not-found.tsx
export default function NotFound() {
return <div>this page will never be rendered</div>;
}
Accessing /home
etc. works but if you enter e.g /test
you'll get the default Next 404 page, instead of the specific not-found.tsx
for that route.
Repro: https://github.com/marlonschlosshauer/next-dynamic-routes-static-params-not-found
Hi @marlonschlosshauer, I've managed to better identify the problem and I've found a workaround, although it's not the best one.
What I've found out is that when /app
router dynamic route throws an error, for some reason the /pages
router 500.js
page is rendered instead of the /app
routers error.js / global-error.js
.
I guess it could be the same for app/not-found.js
, so what you could do is you could add 404.js
page inside the page router, and see if it works.
Hey @bkrajewski94 Thanks for the suggestion! Interestingly enough, that (thankfully?) didn't fix it for me. Running the app in dev mode also indicates that it is still using the global app directory 404 page:
✓ Compiled /[[...slug]] in 434ms (408 modules)
✓ Compiled /not-found in 84ms (413 modules)
Maybe we're having different issues after all. I work around for my issue would be to just use the default /not-found.js
, although I'd like to avoid that to circumvent needing an additional, top-level, layout.
I've played around with it a bit by implementing my own error boundary. I think that the issue is caused by componentDidCatch
not being executed in case of ISR
@marlonschlosshauer It seems like Next.js only recognizes 404 page in root directory and cannot recognize 404 page in dynamic directory.
https://github.com/vercel/next.js/blob/160bb99b06e9c049f88e25806fd995f07f4cc7e1/packages/next/src/build/index.ts#L923-L926
But it is For now
so you can wait until core team to tackle with this issue.
Code above is only a build phase problem, but there are also server side and client side problems and I cannot pick all dependencies of this change. Sorry.
You only can customize root directory 404 page for now. See doc.
Having the same issue as well, with static routes and the app router. If I add a pages folder with 500.tsx, that error component is rendered instead.
Having the same issues when running a Production build. Had to create a _error.js
file in the page router in order to have a custom error show when there is a server side error. The documentation states: If an error is thrown inside a Server Component, Next.js will forward an Error object (stripped of sensitive error information in production) to the nearest error.js file as the error prop.
I don't know if that means that if you have an error.js
in your app router that it is supposed to use that for server errors too or if it just means that it sends through an Error object and we need to somehow handle that.
Funny enough when using the error.js
in App router; when there is a client side error it doesn't display the custom error but instead a blank page and in the console I have a "more hooks rendered than in the previous render" error. Had to write my own ErrorBoundary in order for client errors to work fine. I believe there is some setState in the ErrorBoundary of Next that is misbehaving.
Hopefully they find a fix soon.
+1 . Why don't internal errors jump to app/error.tsx? It seems that only errors thrown in server-side components will jump to app/error.tsx