Skip to content

Commit d3e5c9d

Browse files
committed
Implement error recovery
1 parent 0575bf6 commit d3e5c9d

1 file changed

Lines changed: 178 additions & 84 deletions

File tree

packages/next/src/server/app-render/app-render.tsx

Lines changed: 178 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -4105,7 +4105,6 @@ async function renderToStream(
41054105
metadata.statusCode = res.statusCode
41064106
}
41074107

4108-
// MARK: errorRecovery RSC
41094108
const [errorPreinitScripts, errorBootstrapScript] = getRequiredScripts(
41104109
buildManifest,
41114110
assetPrefix,
@@ -4116,103 +4115,198 @@ async function renderToStream(
41164115
'/_not-found/page'
41174116
)
41184117

4119-
let errorRSCPayload: InitialRSCPayload
4120-
let errorServerStream: import('./stream-ops').AnyStream
4118+
if (process.env.__NEXT_USE_NODE_STREAMS) {
4119+
// MARK: nodeStreams errorRecovery RSC + HTML
4120+
let errorRSCPayload: InitialRSCPayload
4121+
let errorServerStream: import('./stream-ops').AnyStream
41214122

4122-
try {
4123-
errorRSCPayload = await workUnitAsyncStorage.run(
4124-
requestStore,
4125-
getErrorRSCPayload,
4126-
tree,
4127-
ctx,
4128-
reactServerErrorsByDigest.has((err as any).digest) ? null : err,
4129-
errorType
4130-
)
4123+
try {
4124+
errorRSCPayload = await workUnitAsyncStorage.run(
4125+
requestStore,
4126+
getErrorRSCPayload,
4127+
tree,
4128+
ctx,
4129+
reactServerErrorsByDigest.has((err as any).digest) ? null : err,
4130+
errorType
4131+
)
41314132

4132-
errorServerStream = workUnitAsyncStorage.run(
4133-
requestStore,
4134-
renderToWebFlightStream,
4135-
ctx.componentMod,
4136-
errorRSCPayload,
4137-
clientModules,
4138-
{
4139-
filterStackFrame,
4140-
onError: serverComponentsErrorHandler,
4133+
errorServerStream = workUnitAsyncStorage.run(
4134+
requestStore,
4135+
renderToNodeFlightStream,
4136+
ctx.componentMod,
4137+
errorRSCPayload,
4138+
clientModules,
4139+
{
4140+
filterStackFrame,
4141+
onError: serverComponentsErrorHandler,
4142+
}
4143+
)
4144+
4145+
if (reactServerResult === null) {
4146+
endSpanWithError(err)
4147+
throw err
41414148
}
4142-
)
4149+
} catch (setupErr) {
4150+
endSpanWithError(setupErr)
4151+
throw setupErr
4152+
}
4153+
4154+
try {
4155+
const generateStaticHTML =
4156+
supportsDynamicResponse !== true || !!shouldWaitOnAllReady
4157+
4158+
const { stream: errorHtmlStream, allReady: errorAllReady } =
4159+
await workUnitAsyncStorage.run(
4160+
requestStore,
4161+
renderToNodeFizzStream,
4162+
<ErrorApp
4163+
reactServerStream={errorServerStream}
4164+
ServerInsertedHTMLProvider={ServerInsertedHTMLProvider}
4165+
preinitScripts={errorPreinitScripts}
4166+
nonce={nonce}
4167+
images={ctx.renderOpts.images}
4168+
/>,
4169+
{
4170+
nonce,
4171+
bootstrapScriptContent,
4172+
bootstrapScripts: [errorBootstrapScript],
4173+
formState,
4174+
}
4175+
)
4176+
4177+
errorAllReady.finally(() => {
4178+
if (renderSpan.isRecording()) renderSpan.end()
4179+
})
41434180

4144-
if (reactServerResult === null) {
4145-
// We errored when we did not have an RSC stream to read from. This is not just a render
4146-
// error, we need to throw early
4147-
endSpanWithError(err)
4148-
throw err
4181+
return await continueFizzStream(errorHtmlStream, {
4182+
inlinedDataStream: createNodeInlinedDataStream(
4183+
// This is intentionally using the readable datastream from the
4184+
// main render rather than the flight data from the error page
4185+
// render
4186+
reactServerResult.consume(),
4187+
nonce,
4188+
formState
4189+
),
4190+
isStaticGeneration: generateStaticHTML,
4191+
deploymentId: ctx.sharedContext.deploymentId,
4192+
getServerInsertedHTML: makeGetServerInsertedHTML({
4193+
polyfills,
4194+
renderServerInsertedHTML,
4195+
serverCapturedErrors: [],
4196+
basePath,
4197+
tracingMetadata: tracingMetadata,
4198+
}),
4199+
getServerInsertedMetadata,
4200+
validateRootLayout: !!process.env.__NEXT_DEV_SERVER,
4201+
})
4202+
} catch (finalErr: any) {
4203+
if (
4204+
process.env.__NEXT_DEV_SERVER &&
4205+
isHTTPAccessFallbackError(finalErr)
4206+
) {
4207+
const { bailOnRootNotFound } =
4208+
require('../../client/components/dev-root-http-access-fallback-boundary') as typeof import('../../client/components/dev-root-http-access-fallback-boundary')
4209+
bailOnRootNotFound()
4210+
}
4211+
endSpanWithError(finalErr)
4212+
throw finalErr
41494213
}
4150-
} catch (setupErr) {
4151-
endSpanWithError(setupErr)
4152-
throw setupErr
4153-
}
4214+
} else {
4215+
// MARK: webStreams errorRecovery RSC + HTML
4216+
let errorRSCPayload: InitialRSCPayload
4217+
let errorServerStream: import('./stream-ops').AnyStream
41544218

4155-
// MARK: errorRecovery HTML
4156-
try {
4157-
const generateStaticHTML =
4158-
supportsDynamicResponse !== true || !!shouldWaitOnAllReady
4219+
try {
4220+
errorRSCPayload = await workUnitAsyncStorage.run(
4221+
requestStore,
4222+
getErrorRSCPayload,
4223+
tree,
4224+
ctx,
4225+
reactServerErrorsByDigest.has((err as any).digest) ? null : err,
4226+
errorType
4227+
)
41594228

4160-
const { stream: errorHtmlStream, allReady: errorAllReady } =
4161-
await workUnitAsyncStorage.run(
4229+
errorServerStream = workUnitAsyncStorage.run(
41624230
requestStore,
4163-
renderToWebFizzStream,
4164-
<ErrorApp
4165-
reactServerStream={errorServerStream}
4166-
ServerInsertedHTMLProvider={ServerInsertedHTMLProvider}
4167-
preinitScripts={errorPreinitScripts}
4168-
nonce={nonce}
4169-
images={ctx.renderOpts.images}
4170-
/>,
4231+
renderToWebFlightStream,
4232+
ctx.componentMod,
4233+
errorRSCPayload,
4234+
clientModules,
41714235
{
4172-
nonce,
4173-
bootstrapScriptContent,
4174-
bootstrapScripts: [errorBootstrapScript],
4175-
formState,
4236+
filterStackFrame,
4237+
onError: serverComponentsErrorHandler,
41764238
}
41774239
)
41784240

4179-
// End the render span only after React completed rendering (including anything inside Suspense boundaries)
4180-
errorAllReady.finally(() => {
4181-
if (renderSpan.isRecording()) renderSpan.end()
4182-
})
4241+
if (reactServerResult === null) {
4242+
endSpanWithError(err)
4243+
throw err
4244+
}
4245+
} catch (setupErr) {
4246+
endSpanWithError(setupErr)
4247+
throw setupErr
4248+
}
41834249

4184-
return await continueFizzStream(errorHtmlStream, {
4185-
inlinedDataStream: createWebInlinedDataStream(
4186-
// This is intentionally using the readable datastream from the
4187-
// main render rather than the flight data from the error page
4188-
// render
4189-
reactServerResult.consume(),
4190-
nonce,
4191-
formState
4192-
),
4193-
isStaticGeneration: generateStaticHTML,
4194-
deploymentId: ctx.sharedContext.deploymentId,
4195-
getServerInsertedHTML: makeGetServerInsertedHTML({
4196-
polyfills,
4197-
renderServerInsertedHTML,
4198-
serverCapturedErrors: [],
4199-
basePath,
4200-
tracingMetadata: tracingMetadata,
4201-
}),
4202-
getServerInsertedMetadata,
4203-
validateRootLayout: !!process.env.__NEXT_DEV_SERVER,
4204-
})
4205-
} catch (finalErr: any) {
4206-
if (
4207-
process.env.__NEXT_DEV_SERVER &&
4208-
isHTTPAccessFallbackError(finalErr)
4209-
) {
4210-
const { bailOnRootNotFound } =
4211-
require('../../client/components/dev-root-http-access-fallback-boundary') as typeof import('../../client/components/dev-root-http-access-fallback-boundary')
4212-
bailOnRootNotFound()
4250+
try {
4251+
const generateStaticHTML =
4252+
supportsDynamicResponse !== true || !!shouldWaitOnAllReady
4253+
4254+
const { stream: errorHtmlStream, allReady: errorAllReady } =
4255+
await workUnitAsyncStorage.run(
4256+
requestStore,
4257+
renderToWebFizzStream,
4258+
<ErrorApp
4259+
reactServerStream={errorServerStream}
4260+
ServerInsertedHTMLProvider={ServerInsertedHTMLProvider}
4261+
preinitScripts={errorPreinitScripts}
4262+
nonce={nonce}
4263+
images={ctx.renderOpts.images}
4264+
/>,
4265+
{
4266+
nonce,
4267+
bootstrapScriptContent,
4268+
bootstrapScripts: [errorBootstrapScript],
4269+
formState,
4270+
}
4271+
)
4272+
4273+
errorAllReady.finally(() => {
4274+
if (renderSpan.isRecording()) renderSpan.end()
4275+
})
4276+
4277+
return await continueFizzStream(errorHtmlStream, {
4278+
inlinedDataStream: createWebInlinedDataStream(
4279+
// This is intentionally using the readable datastream from the
4280+
// main render rather than the flight data from the error page
4281+
// render
4282+
reactServerResult.consume(),
4283+
nonce,
4284+
formState
4285+
),
4286+
isStaticGeneration: generateStaticHTML,
4287+
deploymentId: ctx.sharedContext.deploymentId,
4288+
getServerInsertedHTML: makeGetServerInsertedHTML({
4289+
polyfills,
4290+
renderServerInsertedHTML,
4291+
serverCapturedErrors: [],
4292+
basePath,
4293+
tracingMetadata: tracingMetadata,
4294+
}),
4295+
getServerInsertedMetadata,
4296+
validateRootLayout: !!process.env.__NEXT_DEV_SERVER,
4297+
})
4298+
} catch (finalErr: any) {
4299+
if (
4300+
process.env.__NEXT_DEV_SERVER &&
4301+
isHTTPAccessFallbackError(finalErr)
4302+
) {
4303+
const { bailOnRootNotFound } =
4304+
require('../../client/components/dev-root-http-access-fallback-boundary') as typeof import('../../client/components/dev-root-http-access-fallback-boundary')
4305+
bailOnRootNotFound()
4306+
}
4307+
endSpanWithError(finalErr)
4308+
throw finalErr
42134309
}
4214-
endSpanWithError(finalErr)
4215-
throw finalErr
42164310
}
42174311
}
42184312
})

0 commit comments

Comments
 (0)