| 351 | // 404 and 500 errors are special kind of errors |
| 352 | // and they are still handle via the main render method. |
| 353 | function renderError(renderErrorProps: RenderErrorProps): Promise<any> { |
| 354 | let { App, err } = renderErrorProps |
| 355 | |
| 356 | // In development runtime errors are caught by our overlay |
| 357 | // In production we catch runtime errors using componentDidCatch which will trigger renderError |
| 358 | if (process.env.NODE_ENV !== 'production') { |
| 359 | // A Next.js rendering runtime error is always unrecoverable |
| 360 | // FIXME: let's make this recoverable (error in GIP client-transition) |
| 361 | devClient.onUnrecoverableError() |
| 362 | |
| 363 | // We need to render an empty <App> so that the `<ReactDevOverlay>` can |
| 364 | // render itself. |
| 365 | return doRender({ |
| 366 | App: () => null, |
| 367 | props: {}, |
| 368 | Component: () => null, |
| 369 | styleSheets: [], |
| 370 | }) |
| 371 | } |
| 372 | |
| 373 | // Make sure we log the error to the console, otherwise users can't track down issues. |
| 374 | console.error(err) |
| 375 | console.error( |
| 376 | `A client-side exception has occurred, see here for more info: https://nextjs.org/docs/messages/client-side-exception-occurred` |
| 377 | ) |
| 378 | |
| 379 | return pageLoader |
| 380 | .loadPage('/_error') |
| 381 | .then(({ page: ErrorComponent, styleSheets }) => { |
| 382 | return lastAppProps?.Component === ErrorComponent |
| 383 | ? import('../pages/_error') |
| 384 | .then((errorModule) => { |
| 385 | return import('../pages/_app').then((appModule) => { |
| 386 | App = appModule.default as any as AppComponent |
| 387 | renderErrorProps.App = App |
| 388 | return errorModule |
| 389 | }) |
| 390 | }) |
| 391 | .then((m) => ({ |
| 392 | ErrorComponent: m.default as React.ComponentType<{}>, |
| 393 | styleSheets: [], |
| 394 | })) |
| 395 | : { ErrorComponent, styleSheets } |
| 396 | }) |
| 397 | .then(({ ErrorComponent, styleSheets }) => { |
| 398 | // In production we do a normal render with the `ErrorComponent` as component. |
| 399 | // If we've gotten here upon initial render, we can use the props from the server. |
| 400 | // Otherwise, we need to call `getInitialProps` on `App` before mounting. |
| 401 | const AppTree = wrapApp(App) |
| 402 | const appCtx = { |
| 403 | Component: ErrorComponent, |
| 404 | AppTree, |
| 405 | router, |
| 406 | ctx: { |
| 407 | err, |
| 408 | pathname: initialData.page, |
| 409 | query: initialData.query, |
| 410 | asPath, |