Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue using _error and 500 page #24206

Closed
bduff9 opened this issue Apr 18, 2021 · 4 comments · Fixed by #23586
Closed

Issue using _error and 500 page #24206

bduff9 opened this issue Apr 18, 2021 · 4 comments · Fixed by #23586
Labels
bug Issue was opened via the bug report template.

Comments

@bduff9
Copy link

bduff9 commented Apr 18, 2021

What version of Next.js are you using?

10.1.3

What version of Node.js are you using?

14.15.4

What browser are you using?

Chrome

What operating system are you using?

macOS

How are you deploying your application?

Vercel

Describe the Bug

I am using Sentry so set up an _error.tsx file in the pages directory. I also need to setup custom 404 and 500 pages with specific branding (which requires a call to the fs module during build). The 404.tsx page works great, but I cannot get the 500.tsx to work during an actual error.

If I go to /500 the page renders great. However, if I set up new page /test/error that just throws an error server side, my 500 page partially renders, but most of the content gets replaced with An unexpected error has occurred.

I tried multiple things to get around it, including moving the entire contents of the 500 to the _error.tsx. However, this fails to build since it seems that fs cannot be used there in the Error.getInitialProps function like it can in the getStaticProps function.

I

Expected Behavior

The documentation does not say that _error and 500 cannot be used together. So I would expect my custom 500 page to render just like my custom 400 page. There seems to be something else going on though since if I delete the custom 500 page, I get a plain white 500 page that just says Internal Server Error regardless of what content I return in my _error.tsx. And if I delete my _error.tsx, the behavior of my custom 500 is the same as described above.

To Reproduce

404.tsx (works):

import { promises as fs } from 'fs';
import path from 'path';

import clsx from 'clsx';
import { GetStaticProps } from 'next';
import Head from 'next/head';
import Image from 'next/image';
import Link from 'next/link';
import { useRouter } from 'next/router';
import React, { FC, useEffect, useMemo } from 'react';
import { gql } from 'graphql-request';
import { useSession } from 'next-auth/client';

import { getPageTitle } from '../utils';
import styles from '../styles/404.module.scss';
import { LogAction, MutationWriteLogArgs } from '../generated/graphql';
import { fetcher } from '../utils/graphql';

type NotFoundProps = {
	images: string[];
};

const NotFound: FC<NotFoundProps> = ({ images }) => {
	const router = useRouter();
	const [session, loading] = useSession();
	const image = useMemo<string>(
		() => images[Math.floor(Math.random() * images.length)],
		// eslint-disable-next-line react-hooks/exhaustive-deps
		[router.asPath],
	);

	useEffect((): void => {
		const writeLog = async (): Promise<void> => {
			const sub = (session?.user.id || null) as null | string;
			const args: MutationWriteLogArgs = {
				data: {
					logAction: LogAction.Error404,
					logMessage: router.asPath,
					sub,
				},
			};
			const query = gql`
				mutation WriteToLog($data: WriteLogInput!) {
					writeLog(data: $data) {
						logID
					}
				}
			`;

			await fetcher<
				{
					writeLog: {
						logID: number;
					};
				},
				MutationWriteLogArgs
			>(query, args);
		};

		if (!loading) {
			writeLog();
		}
	}, [router.asPath, session, loading]);

	return (
		<div className="row">
			<Head>
				<title>{getPageTitle('404')}</title>
			</Head>
			<div className="content-bg text-dark mx-auto mt-6 pb-4 col-md-6">
				<h1 className="text-center">404</h1>
				<div
					className={clsx('mx-auto', 'position-relative', styles['image-404'])}
				>
					<Image
						alt="Branded image"
						layout="fill"
						objectFit="contain"
						objectPosition="center center"
						src={image}
					/>
				</div>
				<h4 className="text-center">
					Something has gone wrong.
				</h4>
				<div className="text-center">
					<Link href="/">
						<a>Please click here to return home</a>
					</Link>
				</div>
			</div>
		</div>
	);
};

// ts-prune-ignore-next
export const getStaticProps: GetStaticProps = async () => {
	const imagesDirectory = path.join(process.cwd(), 'public', '404');
	const imageNames = await fs.readdir(imagesDirectory);
	const images = imageNames.map(image => `/404/${image}`);

	return { props: { images } };
};

NotFound.whyDidYouRender = true;

// ts-prune-ignore-next
export default NotFound;

500.tsx (renders two outer divs, but all other content is replaced with message):

import { promises as fs } from 'fs';
import path from 'path';

import clsx from 'clsx';
import { GetStaticProps } from 'next';
import Head from 'next/head';
import Image from 'next/image';
import Link from 'next/link';
import { useRouter } from 'next/router';
import React, { FC, useMemo } from 'react';
import { useSession } from 'next-auth/client';

import styles from '../styles/500.module.scss';
import { getPageTitle } from '../utils';

type ErrorProps = {
	images: string[];
};

const Error: FC<ErrorProps> = ({ images }) => {
	const router = useRouter();
	const [session, loading] = useSession();
	const image = useMemo<string>(
		() => images[Math.floor(Math.random() * images.length)],
		// eslint-disable-next-line react-hooks/exhaustive-deps
		[router.asPath],
	);

	return (
		<div className="row">
			<Head>
				<title>{getPageTitle('500')}</title>
			</Head>
			<div className="content-bg text-dark mx-auto mt-6 pb-4 col-md-6">
				<h1 className="text-center">Error Occurred</h1>
				<div
					className={clsx('mx-auto', 'position-relative', styles['image-500'])}
				>
					<Image
						alt="Branded image"
						layout="fill"
						objectFit="contain"
						objectPosition="center center"
						src={image}
					/>
				</div>
				<h2 className="text-center">
					There has been an error.
					<br />
					<a
						href="#"
						onClick={(event): false => {
							event.preventDefault();
							router.reload();

							return false;
						}}
					>
						Please try reloading the page
					</a>
				</h2>
				{!loading && <h4 className="text-center">or</h4>}
				{!loading && (
					<h2 className="text-center">
						{session ? (
							<Link href="/">
								<a>Click here to return to your dashboard</a>
							</Link>
						) : (
							<Link href="/auth/login">
								<a>Click here to return to the login page</a>
							</Link>
						)}
					</h2>
				)}
			</div>
		</div>
	);
};

// ts-prune-ignore-next
export const getStaticProps: GetStaticProps = async () => {
	const imagesDirectory = path.join(process.cwd(), 'public', '500');
	const imageNames = await fs.readdir(imagesDirectory);
	const images = imageNames.map(image => `/500/${image}`);

	return { props: { images } };
};

Error.whyDidYouRender = true;

// ts-prune-ignore-next
export default Error;

_error.tsx (Does not seem to render at all)

import * as Sentry from '@sentry/browser';
import { NextPage, NextPageContext } from 'next';
import NextErrorComponent from 'next/error';
import React from 'react';

type ErrorPageProps = {
	err?: unknown;
	hasGetInitialPropsRun?: boolean;
	statusCode: number;
};

const ErrorPage: NextPage<ErrorPageProps> = ({
	err,
	hasGetInitialPropsRun,
	statusCode,
}) => {
	if (!hasGetInitialPropsRun && err) {
		// getInitialProps is not called in case of
		// https://github.com/vercel/next.js/issues/8592. As a workaround, we pass
		// err via _app.js so it can be captured
		Sentry.captureException(err);
	}

	return <NextErrorComponent statusCode={statusCode} />;
};

ErrorPage.getInitialProps = async ({ res, err, asPath }) => {
	const errorInitialProps = await NextErrorComponent.getInitialProps({
		res,
		err,
	} as NextPageContext);

	// Workaround for https://github.com/vercel/next.js/issues/8592, mark when
	// getInitialProps has run
	(errorInitialProps as ErrorPageProps).hasGetInitialPropsRun = true;

	// Running on the server, the response object (`res`) is available.
	//
	// Next.js will pass an err on the server if a page's data fetching methods
	// threw or returned a Promise that rejected
	//
	// Running on the client (browser), Next.js will provide an err if:
	//
	//  - a page's `getInitialProps` threw or returned a Promise that rejected
	//  - an exception was thrown somewhere in the React lifecycle (render,
	//    componentDidMount, etc) that was caught by Next.js's React Error
	//    Boundary. Read more about what types of exceptions are caught by Error
	//    Boundaries: https://reactjs.org/docs/error-boundaries.html

	if (res?.statusCode === 404) {
		// Opinionated: do not record an exception in Sentry for 404
		return { statusCode: 404 };
	}

	if (err) {
		Sentry.captureException(err);
		await Sentry.flush(2000);

		return errorInitialProps;
	}

	// If this point is reached, getInitialProps was called without any
	// information about what the error might be. This is unexpected and may
	// indicate a bug introduced in Next.js, so record it in Sentry
	Sentry.captureException(
		new Error(`_error.js getInitialProps missing data at path: ${asPath}`),
	);

	// Without this try-catch block, builds all fail since
	// Sentry.flush throws `false` here during static builds
	try {
		await Sentry.flush(2000);
	} catch (error) {
		console.log('Sentry.flush failed to be called:', error);
	}

	return errorInitialProps;
};

// ts-prune-ignore-next
export default ErrorPage;

/test/error.tsx (Test error page):

import { GetServerSideProps } from 'next';
import React, { FC } from 'react';

const TestError: FC = () => <h1>Test Page</h1>;

export const getServerSideProps: GetServerSideProps = async () => {
	throw new Error('Testing 500 page');
};

// ts-prune-ignore-next
export default TestError;
@bduff9 bduff9 added the bug Issue was opened via the bug report template. label Apr 18, 2021
@txm3278
Copy link

txm3278 commented Apr 19, 2021

This issue is appearing on the latest release build, 10.2.0, as well.

@jonathanwelton
Copy link

I also have this issue.

I'm trying to use a custom _error page, which works locally when you visit /throw (a page that throws an error), but neither Netlify or Vercel respect it when the app is deployed. Similar to @bduff9, I just receive a stack trace, which you can see here: https://60c22a5aaf61f200073ca417--nextjs-custom-error-test.netlify.app/throw

I've been experimenting with a repo: https://github.com/jonathanwelton/nextjs-custom-error-test

Reverting to Next.js 9.4.4, the custom _error page works on Vercel: https://nextjs-custom-error-test.vercel.app/throw

But it doesn't appear you can deploy a Next.js app below version 10 on Netlify.

@chris-erickson
Copy link

Same thing here on Netlify, been following this guide as exactly as I can: https://github.com/vercel/next.js/tree/canary/examples/with-sentry

@kodiakhq kodiakhq bot closed this as completed in #23586 Jun 11, 2021
kodiakhq bot pushed a commit that referenced this issue Jun 11, 2021
This prevents unexpected errors occurring from users leveraging `getServerSideProps` in `_error` with the new `/500` prerendering as we are currently only checking for a custom `getInitialProps` in `_error` since it opts out of the static optimization although `getServerSideProps` also opts out of the optimization. 

Fixes: #23541
Fixes: #23128
Fixes: #23541
Fixes: #24206

## Bug

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
janicklas-ralph pushed a commit to janicklas-ralph/next.js that referenced this issue Jun 11, 2021
…23586)

This prevents unexpected errors occurring from users leveraging `getServerSideProps` in `_error` with the new `/500` prerendering as we are currently only checking for a custom `getInitialProps` in `_error` since it opts out of the static optimization although `getServerSideProps` also opts out of the optimization. 

Fixes: vercel#23541
Fixes: vercel#23128
Fixes: vercel#23541
Fixes: vercel#24206

## Bug

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
flybayer pushed a commit to blitz-js/next.js that referenced this issue Jun 16, 2021
…23586)

This prevents unexpected errors occurring from users leveraging `getServerSideProps` in `_error` with the new `/500` prerendering as we are currently only checking for a custom `getInitialProps` in `_error` since it opts out of the static optimization although `getServerSideProps` also opts out of the optimization. 

Fixes: vercel#23541
Fixes: vercel#23128
Fixes: vercel#23541
Fixes: vercel#24206

## Bug

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
@balazsorban44
Copy link
Member

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@vercel vercel locked as resolved and limited conversation to collaborators Jan 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Issue was opened via the bug report template.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants