diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index c2c629f526150..514a4ecc189a3 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -68,7 +68,6 @@ import { createHTMLReactServerErrorHandler, createHTMLErrorHandler, type DigestedError, - isUserLandError, } from './create-error-handler' import { getShortDynamicParamType, @@ -977,14 +976,7 @@ async function renderToHTMLOrFlightImpl( // prerendering phase and the build. if (response.digestErrorsMap.size) { const buildFailingError = response.digestErrorsMap.values().next().value - if (buildFailingError) throw buildFailingError - } - // Pick first userland SSR error, which is also not a RSC error. - if (response.ssrErrors.length) { - const buildFailingError = response.ssrErrors.find((err) => - isUserLandError(err) - ) - if (buildFailingError) throw buildFailingError + throw buildFailingError } const options: RenderResultOptions = { @@ -1237,29 +1229,26 @@ async function renderToStream( const reactServerErrorsByDigest: Map = new Map() const silenceLogger = false - function onHTMLRenderRSCError(err: DigestedError) { - return renderOpts.onInstrumentationRequestError?.( - err, - req, - createErrorContext(ctx, 'react-server-components') - ) - } const serverComponentsErrorHandler = createHTMLReactServerErrorHandler( !!renderOpts.dev, !!renderOpts.nextExport, reactServerErrorsByDigest, silenceLogger, - onHTMLRenderRSCError + // RSC rendering error will report as SSR error + // @TODO we should report RSC errors where they happen for instrumentation purposes + // and should omit the error reporter in the SSR layer instead + undefined ) - - function onHTMLRenderSSRError(err: DigestedError) { + function onServerRenderError(err: DigestedError) { + const renderSource = reactServerErrorsByDigest.has(err.digest) + ? 'react-server-components' + : 'server-rendering' return renderOpts.onInstrumentationRequestError?.( err, req, - createErrorContext(ctx, 'server-rendering') + createErrorContext(ctx, renderSource) ) } - const allCapturedErrors: Array = [] const htmlRendererErrorHandler = createHTMLErrorHandler( !!renderOpts.dev, @@ -1267,7 +1256,7 @@ async function renderToStream( reactServerErrorsByDigest, allCapturedErrors, silenceLogger, - onHTMLRenderSSRError + onServerRenderError ) let primaryRenderReactServerStream: null | ReadableStream = null @@ -1586,7 +1575,6 @@ async function renderToStream( type PrenderToStringResult = { stream: ReadableStream digestErrorsMap: Map - ssrErrors: Array dynamicTracking?: null | DynamicTrackingState err?: unknown } @@ -1651,26 +1639,24 @@ async function prerenderToStream( const reactServerErrorsByDigest: Map = new Map() // We don't report errors during prerendering through our instrumentation hooks const silenceLogger = !!renderOpts.experimental.isRoutePPREnabled - function onHTMLRenderRSCError(err: DigestedError) { - return renderOpts.onInstrumentationRequestError?.( - err, - req, - createErrorContext(ctx, 'react-server-components') - ) - } const serverComponentsErrorHandler = createHTMLReactServerErrorHandler( !!renderOpts.dev, !!renderOpts.nextExport, reactServerErrorsByDigest, silenceLogger, - onHTMLRenderRSCError + // RSC rendering error will report as SSR error + // @TODO we should report RSC errors where they happen for instrumentation purposes + // and should omit the error reporter in the SSR layer instead + undefined ) - - function onHTMLRenderSSRError(err: DigestedError) { + function onServerRenderError(err: DigestedError) { + const renderSource = reactServerErrorsByDigest.has(err.digest) + ? 'react-server-components' + : 'server-rendering' return renderOpts.onInstrumentationRequestError?.( err, req, - createErrorContext(ctx, 'server-rendering') + createErrorContext(ctx, renderSource) ) } const allCapturedErrors: Array = [] @@ -1680,7 +1666,7 @@ async function prerenderToStream( reactServerErrorsByDigest, allCapturedErrors, silenceLogger, - onHTMLRenderSSRError + onServerRenderError ) let dynamicTracking: null | DynamicTrackingState = null @@ -1804,7 +1790,6 @@ async function prerenderToStream( // require the same set so we unify the code path here return { digestErrorsMap: reactServerErrorsByDigest, - ssrErrors: allCapturedErrors, stream: await continueDynamicPrerender(prelude, { getServerInsertedHTML, }), @@ -1852,7 +1837,6 @@ async function prerenderToStream( return { digestErrorsMap: reactServerErrorsByDigest, - ssrErrors: allCapturedErrors, stream: await continueStaticPrerender(htmlStream, { inlinedDataStream: createInlinedDataReadableStream( inlinedReactServerDataStream, @@ -1922,7 +1906,6 @@ async function prerenderToStream( }) return { digestErrorsMap: reactServerErrorsByDigest, - ssrErrors: allCapturedErrors, stream: await continueFizzStream(htmlStream, { inlinedDataStream: createInlinedDataReadableStream( inlinedReactServerDataStream, @@ -2061,7 +2044,6 @@ async function prerenderToStream( // the response in the caller. err, digestErrorsMap: reactServerErrorsByDigest, - ssrErrors: allCapturedErrors, stream: await continueFizzStream(fizzStream, { inlinedDataStream: createInlinedDataReadableStream( // This is intentionally using the readable datastream from the diff --git a/packages/next/src/server/app-render/create-error-handler.tsx b/packages/next/src/server/app-render/create-error-handler.tsx index 9db7cbbc49f18..e3b030dfb793d 100644 --- a/packages/next/src/server/app-render/create-error-handler.tsx +++ b/packages/next/src/server/app-render/create-error-handler.tsx @@ -139,21 +139,19 @@ export function createHTMLErrorHandler( dev: boolean, isNextExport: boolean, reactServerErrors: Map, - allCapturedErrors: Array, + allCapturedError: Array, silenceLogger: boolean, - onHTMLRenderSSRError: (err: any) => void + onHTMLRenderError: (err: any) => void ): ErrorHandler { return (err: any, errorInfo: any) => { - let isSSRError = true - // If the error already has a digest, respect the original digest, // so it won't get re-generated into another new error. + if (err.digest) { if (reactServerErrors.has(err.digest)) { // This error is likely an obfuscated error from react-server. // We recover the original error here. err = reactServerErrors.get(err.digest) - isSSRError = false } else { // The error is not from react-server but has a digest // from other means so we don't need to produce a new one @@ -164,7 +162,7 @@ export function createHTMLErrorHandler( ).toString() } - allCapturedErrors.push(err) + allCapturedError.push(err) // If the response was closed, we don't need to log the error. if (isAbortError(err)) return @@ -205,21 +203,11 @@ export function createHTMLErrorHandler( }) } - if ( - !silenceLogger && - // HTML errors contain RSC errors as well, filter them out before reporting - isSSRError - ) { - onHTMLRenderSSRError(err) + if (!silenceLogger) { + onHTMLRenderError(err) } } return err.digest } } - -export function isUserLandError(err: any): boolean { - return ( - !isAbortError(err) && !isBailoutToCSRError(err) && !isNextRouterError(err) - ) -} diff --git a/test/production/app-dir/client-page-error-bailout/app/layout.js b/test/production/app-dir/client-page-error-bailout/app/layout.js deleted file mode 100644 index a3a86a5ca1e12..0000000000000 --- a/test/production/app-dir/client-page-error-bailout/app/layout.js +++ /dev/null @@ -1,7 +0,0 @@ -export default function Root({ children }) { - return ( - - {children} - - ) -} diff --git a/test/production/app-dir/client-page-error-bailout/app/page.js b/test/production/app-dir/client-page-error-bailout/app/page.js deleted file mode 100644 index 22570eae6c578..0000000000000 --- a/test/production/app-dir/client-page-error-bailout/app/page.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client' - -export default function Page() { - throw new Error('client-page-error') -} diff --git a/test/production/app-dir/client-page-error-bailout/client-page-error-bailout.test.ts b/test/production/app-dir/client-page-error-bailout/client-page-error-bailout.test.ts deleted file mode 100644 index 1c36d86023be2..0000000000000 --- a/test/production/app-dir/client-page-error-bailout/client-page-error-bailout.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { nextTestSetup } from 'e2e-utils' - -describe('app-dir - client-page-error-bailout', () => { - const { next, skipped } = nextTestSetup({ - files: __dirname, - skipStart: true, - }) - - if (skipped) { - return - } - - let stderr = '' - beforeAll(() => { - const onLog = (log: string) => { - stderr += log - } - - next.on('stderr', onLog) - }) - - it('should bail out in static generation build', async () => { - await next.build() - expect(stderr).toContain( - 'Error occurred prerendering page "/". Read more: https://nextjs.org/docs/messages/prerender-error' - ) - expect(stderr).toContain('Error: client-page-error') - }) -})