Repository: next.js
Issue: Inconsistent Exception/Error Handling (500 Internal Server Error / custom error / _error.js)
Labels: Navigation

# Bug report

Depending on where an error occurs, I've noticed at least 4 different behaviours that happen in response to an error:

I've created a minimal app with the following files: `pages/_app.js`, `pages/_error.js`, `pages/[[...slug]].js` and tested the following (in production build):

1. **`throw` in `_app.js` `getInitialProps`**
   - **response on page load:**  `Internal Server Error` (string, no html document)
   - **response on (client side) navigation:** redirect/page-reload and then `Internal Server Error` (string, html document)
1. **`throw` in `_app.js` render**
   - **response on page load:**  `Internal Server Error` (string, no html document)
   - **response on (client side) navigation:** exception happens client side, resulting in endless loop of react trying to render the component (browser becomes unresponsive)
1. **`throw` in `pages/[[...slug]].js` `getServerSideProps`**
   - **response on page load:**  content of `_error.js`
   - **response on (client side) navigation:** redirect/page-reload and then content of `_error.js`
1. **`throw` in `pages/[[...slug]].js` render (or any sub component)**
   - **response on page load:**  content of `_error.js`
   - **response on (client side) navigation:** content of `_error.js`
1. api routes will crash next, see #17574


## To Reproduce

```js
// pages/_app.js
import Link from 'next/link'

function App({Component, pageProps, router}) {
	if (router.query.testerror === 'app') {
		throw '[testerror] app';
	}
	return (<>
		<div style={{padding: 20}}>
			{['/', '/?testerror=app', '/?testerror=app-getInitialProps', '/?testerror=page', '/?testerror=page-getServerSideProps'].map((url, i) => (
				<>
					<Link key={url} href={url}><a>{url}</a></Link>
					{' | '}
				</>
			))}
		</div>
		{pageProps?.myStatusCode ? `pageProps.statusCode in _app.js = ${pageProps?.myStatusCode}` : <Component {...pageProps} />}
	</>)
}

App.getInitialProps = async ({router}) => {
	if (router.query.testerror === 'app-getInitialProps') {
		throw '[testerror] app-getInitialProps';
	}
	return {}
}

export default App
```
```js
// pages/_error.js
function Error(props) {
	return <div>
		<h1>_error.js</h1>
		<pre>{JSON.stringify(props, null, 4)}</pre>
	</div>
}

Error.getInitialProps = ({res, err}) => {
	const statusCode = res ? res.statusCode : err ? err.statusCode : 404
	return {statusCode}
}

export default Error
```
```js
// pages/[[...slug]].js
import {useRouter} from "next/router";

export default function RequestHandler(props) {
	const {query} = useRouter()
	if (query.testerror === 'page') {
		throw '[testerror] page';
	}
	return (
		<div>
			Page
			<pre>{JSON.stringify(props, null, 4)}</pre>
		</div>
	)
}

export async function getServerSideProps({params, req, res, query, preview, previewData, resolvedUrl}) {
	if (query.testerror === 'page-getServerSideProps') {
		throw '[testerror] page-getServerSideProps';
	}
	if (!params.slug || ['page-a', 'page-b'].includes(params.slug[0])) {
		return {
			props: {
				content: `This is the ${params.slug ? params.slug[0] : 'index'} page`
			}
		}
	}
	if (!req.url.match(/^\/_next\/data\/.*slug=.*$/)) {
		// only set status code 404 if it's a page load, not for XHR, see #18185
		res.statusCode = 404
	}
	return {
		props: {
			myStatusCode: 404,
			myMessage: `couldn't find page`
		}
	}
}
```
Example code includes Links to different URLs with exceptions

## Expected behavior

- exceptions in _app.js should also be caught and handled the same as in pages
- exceptions in app render should not cause a rendering loop which makes the  browser become unresponsive
- failing the above two points, we should update the documentation to mention that there are cases where custom error handling will not work

And if the mentality is really like described in the first comment on  #17574 "let it crash", then why do we have `_error.js`. I'm not arguing for either side, but as a project next should commit to one way and do it as consistently as possible (and clearly document cases where it's not consistent).

## System information

- Version of Next.js: 9.5.5
- Version of Node.js: v14.12.0