Skip to content

Commit 3b8cd92

Browse files
authored
Fix error logging for 'use cache' runtime errors in production (#86500)
In production, when throwing an error in `'use cache'` at runtime, we are currently logging the obfuscated error that React is producing when crossing the cache-server boundary. This is not ideal for investigating production issues so we're also logging the original error in the `'use cache'` wrapper as a work-around. But this error does not have a digest, which makes it non-obvious that it's the same error as the obfuscated one. With this PR we are fixing this by storing the original error (with a digest) in the `reactServerErrorsByDigest` map (moved to the work store), and retrieving it in the server environment when we log the error. Thus the obfuscated error is not logged, and the original error with a digest is logged instead. In development, we keep the existing behavior of logging the transported error, which also includes the dev-only `environmentName` property `'Cache'`. As part of this fix, we're consolidating the `createFlightReactServerErrorHandler` and `createHTMLReactServerErrorHandler` functions, which had a big overlap and confusing names, into a single `createReactServerErrorHandler` function. closes NAR-536
1 parent 13ae351 commit 3b8cd92

File tree

20 files changed

+371
-204
lines changed

20 files changed

+371
-204
lines changed

packages/next/src/build/templates/app-page.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,11 +606,17 @@ export async function handler(
606606
},
607607
onAfterTaskError: () => {},
608608

609-
onInstrumentationRequestError: (error, _request, errorContext) =>
609+
onInstrumentationRequestError: (
610+
error,
611+
_request,
612+
errorContext,
613+
silenceLog
614+
) =>
610615
routeModule.onRequestError(
611616
req,
612617
error,
613618
errorContext,
619+
silenceLog,
614620
routerServerContext
615621
),
616622
err: getRequestMeta(req, 'invokeError'),
@@ -1414,6 +1420,7 @@ export async function handler(
14141420
}
14151421
} catch (err) {
14161422
if (!(err instanceof NoFallbackError)) {
1423+
const silenceLog = false
14171424
await routeModule.onRequestError(
14181425
req,
14191426
err,
@@ -1426,6 +1433,7 @@ export async function handler(
14261433
isOnDemandRevalidate,
14271434
}),
14281435
},
1436+
silenceLog,
14291437
routerServerContext
14301438
)
14311439
}

packages/next/src/build/templates/app-route.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,17 @@ export async function handler(
215215
res.on('close', cb)
216216
},
217217
onAfterTaskError: undefined,
218-
onInstrumentationRequestError: (error, _request, errorContext) =>
218+
onInstrumentationRequestError: (
219+
error,
220+
_request,
221+
errorContext,
222+
silenceLog
223+
) =>
219224
routeModule.onRequestError(
220225
req,
221226
error,
222227
errorContext,
228+
silenceLog,
223229
routerServerContext
224230
),
225231
},
@@ -369,6 +375,7 @@ export async function handler(
369375
// if this is a background revalidate we need to report
370376
// the request error here as it won't be bubbled
371377
if (previousCacheEntry?.isStale) {
378+
const silenceLog = false
372379
await routeModule.onRequestError(
373380
req,
374381
err,
@@ -381,6 +388,7 @@ export async function handler(
381388
isOnDemandRevalidate,
382389
}),
383390
},
391+
silenceLog,
384392
routerServerContext
385393
)
386394
}
@@ -488,15 +496,22 @@ export async function handler(
488496
}
489497
} catch (err) {
490498
if (!(err instanceof NoFallbackError)) {
491-
await routeModule.onRequestError(req, err, {
492-
routerKind: 'App Router',
493-
routePath: normalizedSrcPage,
494-
routeType: 'route',
495-
revalidateReason: getRevalidateReason({
496-
isStaticGeneration,
497-
isOnDemandRevalidate,
498-
}),
499-
})
499+
const silenceLog = false
500+
await routeModule.onRequestError(
501+
req,
502+
err,
503+
{
504+
routerKind: 'App Router',
505+
routePath: normalizedSrcPage,
506+
routeType: 'route',
507+
revalidateReason: getRevalidateReason({
508+
isStaticGeneration,
509+
isOnDemandRevalidate,
510+
}),
511+
},
512+
silenceLog,
513+
routerServerContext
514+
)
500515
}
501516

502517
// rethrow so that we can handle serving error page

packages/next/src/build/templates/edge-ssr-app.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,17 @@ async function requestHandler(
180180
},
181181
onAfterTaskError: () => {},
182182

183-
onInstrumentationRequestError: (error, _request, errorContext) =>
183+
onInstrumentationRequestError: (
184+
error,
185+
_request,
186+
errorContext,
187+
silenceLog
188+
) =>
184189
pageRouteModule.onRequestError(
185190
baseReq,
186191
error,
187192
errorContext,
193+
silenceLog,
188194
routerServerContext
189195
),
190196
dev: pageRouteModule.isDev,
@@ -319,12 +325,18 @@ async function requestHandler(
319325

320326
return renderResultToResponse(result)
321327
} catch (err) {
322-
await pageRouteModule.onRequestError(baseReq, err, {
323-
routerKind: 'App Router',
324-
routePath: normalizedSrcPage,
325-
routeType: 'render',
326-
revalidateReason: undefined,
327-
})
328+
const silenceLog = false
329+
await pageRouteModule.onRequestError(
330+
baseReq,
331+
err,
332+
{
333+
routerKind: 'App Router',
334+
routePath: normalizedSrcPage,
335+
routeType: 'render',
336+
revalidateReason: undefined,
337+
},
338+
silenceLog
339+
)
328340
// rethrow so that we can handle serving error page
329341
throw err
330342
}

packages/next/src/build/templates/edge-ssr.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -301,12 +301,18 @@ async function requestHandler(
301301
throw err
302302
}
303303

304-
await errRouteModule.onRequestError(baseReq, err, {
305-
routerKind: 'Pages Router',
306-
routePath: srcPage,
307-
routeType: 'render',
308-
revalidateReason: undefined,
309-
})
304+
const silenceLog = false
305+
await errRouteModule.onRequestError(
306+
baseReq,
307+
err,
308+
{
309+
routerKind: 'Pages Router',
310+
routePath: srcPage,
311+
routeType: 'render',
312+
revalidateReason: undefined,
313+
},
314+
silenceLog
315+
)
310316

311317
const errResult = await errRouteModule.render(
312318
// @ts-expect-error we don't type this for edge

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

Lines changed: 60 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ import { AppRenderSpan, NextNodeServerSpan } from '../lib/trace/constants'
7878
import { getTracer } from '../lib/trace/tracer'
7979
import { FlightRenderResult } from './flight-render-result'
8080
import {
81-
createFlightReactServerErrorHandler,
82-
createHTMLReactServerErrorHandler,
81+
createReactServerErrorHandler,
8382
createHTMLErrorHandler,
8483
type DigestedError,
8584
isUserLandError,
@@ -611,17 +610,22 @@ async function generateDynamicFlightRenderResult(
611610
dev = false,
612611
onInstrumentationRequestError,
613612
setReactDebugChannel,
613+
nextExport = false,
614614
} = renderOpts
615615

616-
function onFlightDataRenderError(err: DigestedError) {
616+
function onFlightDataRenderError(err: DigestedError, silenceLog: boolean) {
617617
return onInstrumentationRequestError?.(
618618
err,
619619
req,
620-
createErrorContext(ctx, 'react-server-components-payload')
620+
createErrorContext(ctx, 'react-server-components-payload'),
621+
silenceLog
621622
)
622623
}
623-
const onError = createFlightReactServerErrorHandler(
624+
625+
const onError = createReactServerErrorHandler(
624626
dev,
627+
nextExport,
628+
workStore.reactServerErrorsByDigest,
625629
onFlightDataRenderError
626630
)
627631

@@ -766,18 +770,23 @@ async function generateDynamicFlightRenderResultWithStagesInDev(
766770
setReactDebugChannel,
767771
setCacheStatus,
768772
clientReferenceManifest,
773+
nextExport = false,
769774
} = renderOpts
770775
assertClientReferenceManifest(clientReferenceManifest)
771776

772-
function onFlightDataRenderError(err: DigestedError) {
777+
function onFlightDataRenderError(err: DigestedError, silenceLog: boolean) {
773778
return onInstrumentationRequestError?.(
774779
err,
775780
req,
776-
createErrorContext(ctx, 'react-server-components-payload')
781+
createErrorContext(ctx, 'react-server-components-payload'),
782+
silenceLog
777783
)
778784
}
779-
const onError = createFlightReactServerErrorHandler(
785+
786+
const onError = createReactServerErrorHandler(
780787
dev,
788+
nextExport,
789+
workStore.reactServerErrorsByDigest,
781790
onFlightDataRenderError
782791
)
783792

@@ -909,19 +918,23 @@ async function generateRuntimePrefetchResult(
909918
ctx: AppRenderContext,
910919
requestStore: RequestStore
911920
): Promise<RenderResult> {
912-
const { workStore } = ctx
913-
const renderOpts = ctx.renderOpts
921+
const { workStore, renderOpts } = ctx
922+
const { nextExport = false, onInstrumentationRequestError } = renderOpts
914923

915-
function onFlightDataRenderError(err: DigestedError) {
916-
return renderOpts.onInstrumentationRequestError?.(
924+
function onFlightDataRenderError(err: DigestedError, silenceLog: boolean) {
925+
return onInstrumentationRequestError?.(
917926
err,
918927
req,
919928
// TODO(runtime-ppr): should we use a different value?
920-
createErrorContext(ctx, 'react-server-components-payload')
929+
createErrorContext(ctx, 'react-server-components-payload'),
930+
silenceLog
921931
)
922932
}
923-
const onError = createFlightReactServerErrorHandler(
933+
934+
const onError = createReactServerErrorHandler(
924935
false,
936+
nextExport,
937+
workStore.reactServerErrorsByDigest,
925938
onFlightDataRenderError
926939
)
927940

@@ -2601,28 +2614,31 @@ async function renderToStream(
26012614
? `self.__next_r=${JSON.stringify(requestId)}`
26022615
: undefined
26032616

2604-
const reactServerErrorsByDigest: Map<string, DigestedError> = new Map()
2605-
const silenceLogger = false
2606-
function onHTMLRenderRSCError(err: DigestedError) {
2617+
const { reactServerErrorsByDigest } = workStore
2618+
function onHTMLRenderRSCError(err: DigestedError, silenceLog: boolean) {
26072619
return onInstrumentationRequestError?.(
26082620
err,
26092621
req,
2610-
createErrorContext(ctx, 'react-server-components')
2622+
createErrorContext(ctx, 'react-server-components'),
2623+
silenceLog
26112624
)
26122625
}
2613-
const serverComponentsErrorHandler = createHTMLReactServerErrorHandler(
2626+
const serverComponentsErrorHandler = createReactServerErrorHandler(
26142627
dev,
26152628
nextExport,
26162629
reactServerErrorsByDigest,
2617-
silenceLogger,
26182630
onHTMLRenderRSCError
26192631
)
26202632

26212633
function onHTMLRenderSSRError(err: DigestedError) {
2634+
// We don't need to silence logs here. onHTMLRenderSSRError won't be called
2635+
// at all if the error was logged before in the RSC error handler.
2636+
const silenceLog = false
26222637
return onInstrumentationRequestError?.(
26232638
err,
26242639
req,
2625-
createErrorContext(ctx, 'server-rendering')
2640+
createErrorContext(ctx, 'server-rendering'),
2641+
silenceLog
26262642
)
26272643
}
26282644

@@ -2632,7 +2648,6 @@ async function renderToStream(
26322648
nextExport,
26332649
reactServerErrorsByDigest,
26342650
allCapturedErrors,
2635-
silenceLogger,
26362651
onHTMLRenderSSRError
26372652
)
26382653

@@ -4145,38 +4160,45 @@ async function prerenderToStream(
41454160
page
41464161
)
41474162

4148-
const reactServerErrorsByDigest: Map<string, DigestedError> = new Map()
4163+
const { reactServerErrorsByDigest } = workStore
41494164
// We don't report errors during prerendering through our instrumentation hooks
4150-
const silenceLogger = !!experimental.isRoutePPREnabled
4151-
function onHTMLRenderRSCError(err: DigestedError) {
4152-
return onInstrumentationRequestError?.(
4153-
err,
4154-
req,
4155-
createErrorContext(ctx, 'react-server-components')
4156-
)
4165+
const reportErrors = !experimental.isRoutePPREnabled
4166+
function onHTMLRenderRSCError(err: DigestedError, silenceLog: boolean) {
4167+
if (reportErrors) {
4168+
return onInstrumentationRequestError?.(
4169+
err,
4170+
req,
4171+
createErrorContext(ctx, 'react-server-components'),
4172+
silenceLog
4173+
)
4174+
}
41574175
}
4158-
const serverComponentsErrorHandler = createHTMLReactServerErrorHandler(
4176+
const serverComponentsErrorHandler = createReactServerErrorHandler(
41594177
dev,
41604178
nextExport,
41614179
reactServerErrorsByDigest,
4162-
silenceLogger,
41634180
onHTMLRenderRSCError
41644181
)
41654182

41664183
function onHTMLRenderSSRError(err: DigestedError) {
4167-
return onInstrumentationRequestError?.(
4168-
err,
4169-
req,
4170-
createErrorContext(ctx, 'server-rendering')
4171-
)
4184+
if (reportErrors) {
4185+
// We don't need to silence logs here. onHTMLRenderSSRError won't be
4186+
// called at all if the error was logged before in the RSC error handler.
4187+
const silenceLog = false
4188+
return onInstrumentationRequestError?.(
4189+
err,
4190+
req,
4191+
createErrorContext(ctx, 'server-rendering'),
4192+
silenceLog
4193+
)
4194+
}
41724195
}
41734196
const allCapturedErrors: Array<unknown> = []
41744197
const htmlRendererErrorHandler = createHTMLErrorHandler(
41754198
dev,
41764199
nextExport,
41774200
reactServerErrorsByDigest,
41784201
allCapturedErrors,
4179-
silenceLogger,
41804202
onHTMLRenderSSRError
41814203
)
41824204

0 commit comments

Comments
 (0)