Skip to content

Commit a5d8b9c

Browse files
committed
Add a prefix to errors serialized from server rendering
It can be a bit confusing to see where this error came from otherwise since it didn't come from elsewhere on the client.
1 parent 73a50cb commit a5d8b9c

File tree

4 files changed

+89
-59
lines changed

4 files changed

+89
-59
lines changed

packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,8 @@ describe('ReactDOMFizzServer', () => {
786786
errors,
787787
[
788788
[
789-
theError.message,
789+
'Switched to client rendering because the server rendering errored:\n\n' +
790+
theError.message,
790791
expectedDigest,
791792
componentStack(['Lazy', 'Suspense', 'div', 'App']),
792793
],
@@ -909,7 +910,8 @@ describe('ReactDOMFizzServer', () => {
909910
errors,
910911
[
911912
[
912-
theError.message,
913+
'Switched to client rendering because the server rendering errored:\n\n' +
914+
theError.message,
913915
expectedDigest,
914916
componentStack(['Lazy', 'Suspense', 'div', 'App']),
915917
],
@@ -992,7 +994,8 @@ describe('ReactDOMFizzServer', () => {
992994
errors,
993995
[
994996
[
995-
theError.message,
997+
'Switched to client rendering because the server rendering errored:\n\n' +
998+
theError.message,
996999
expectedDigest,
9971000
componentStack([
9981001
'Erroring',
@@ -1078,7 +1081,8 @@ describe('ReactDOMFizzServer', () => {
10781081
errors,
10791082
[
10801083
[
1081-
theError.message,
1084+
'Switched to client rendering because the server rendering errored:\n\n' +
1085+
theError.message,
10821086
expectedDigest,
10831087
componentStack(['Lazy', 'Suspense', 'div', 'App']),
10841088
],
@@ -1404,13 +1408,15 @@ describe('ReactDOMFizzServer', () => {
14041408
errors,
14051409
[
14061410
[
1407-
'The server did not finish this Suspense boundary: The render was aborted by the server without a reason.',
1411+
'Switched to client rendering because the server rendering aborted due to:\n\n' +
1412+
'The render was aborted by the server without a reason.',
14081413
expectedDigest,
14091414
// We get the stack of the task when it was aborted which is why we see `h1`
14101415
componentStack(['h1', 'Suspense', 'div', 'App']),
14111416
],
14121417
[
1413-
'The server did not finish this Suspense boundary: The render was aborted by the server without a reason.',
1418+
'Switched to client rendering because the server rendering aborted due to:\n\n' +
1419+
'The render was aborted by the server without a reason.',
14141420
expectedDigest,
14151421
componentStack(['Suspense', 'main', 'div', 'App']),
14161422
],
@@ -2145,7 +2151,8 @@ describe('ReactDOMFizzServer', () => {
21452151
errors,
21462152
[
21472153
[
2148-
theError.message,
2154+
'Switched to client rendering because the server rendering errored:\n\n' +
2155+
theError.message,
21492156
expectedDigest,
21502157
componentStack([
21512158
'AsyncText',
@@ -3431,12 +3438,14 @@ describe('ReactDOMFizzServer', () => {
34313438
errors,
34323439
[
34333440
[
3434-
'The server did not finish this Suspense boundary: foobar',
3441+
'Switched to client rendering because the server rendering aborted due to:\n\n' +
3442+
'foobar',
34353443
'a digest',
34363444
componentStack(['Suspense', 'p', 'div', 'App']),
34373445
],
34383446
[
3439-
'The server did not finish this Suspense boundary: foobar',
3447+
'Switched to client rendering because the server rendering aborted due to:\n\n' +
3448+
'foobar',
34403449
'a digest',
34413450
componentStack(['Suspense', 'span', 'div', 'App']),
34423451
],
@@ -3512,12 +3521,14 @@ describe('ReactDOMFizzServer', () => {
35123521
errors,
35133522
[
35143523
[
3515-
'The server did not finish this Suspense boundary: uh oh',
3524+
'Switched to client rendering because the server rendering aborted due to:\n\n' +
3525+
'uh oh',
35163526
'a digest',
35173527
componentStack(['Suspense', 'p', 'div', 'App']),
35183528
],
35193529
[
3520-
'The server did not finish this Suspense boundary: uh oh',
3530+
'Switched to client rendering because the server rendering aborted due to:\n\n' +
3531+
'uh oh',
35213532
'a digest',
35223533
componentStack(['Suspense', 'span', 'div', 'App']),
35233534
],
@@ -3991,7 +4002,8 @@ describe('ReactDOMFizzServer', () => {
39914002
errors,
39924003
[
39934004
[
3994-
theError.message,
4005+
'Switched to client rendering because the server rendering errored:\n\n' +
4006+
theError.message,
39954007
expectedDigest,
39964008
componentStack(['Erroring', 'Suspense', 'div', 'App']),
39974009
],
@@ -6772,7 +6784,14 @@ describe('ReactDOMFizzServer', () => {
67726784

67736785
expect(recoverableErrors).toEqual(
67746786
__DEV__
6775-
? ['server error', 'replay error', 'server error']
6787+
? [
6788+
'Switched to client rendering because the server rendering errored:\n\n' +
6789+
'server error',
6790+
'Switched to client rendering because the server rendering errored:\n\n' +
6791+
'replay error',
6792+
'Switched to client rendering because the server rendering errored:\n\n' +
6793+
'server error',
6794+
]
67766795
: [
67776796
'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
67786797
'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
@@ -6931,8 +6950,10 @@ describe('ReactDOMFizzServer', () => {
69316950
expect(recoverableErrors).toEqual(
69326951
__DEV__
69336952
? [
6934-
'The server did not finish this Suspense boundary: aborted',
6935-
'The server did not finish this Suspense boundary: aborted',
6953+
'Switched to client rendering because the server rendering aborted due to:\n\n' +
6954+
'aborted',
6955+
'Switched to client rendering because the server rendering aborted due to:\n\n' +
6956+
'aborted',
69366957
]
69376958
: [
69386959
'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
@@ -7103,8 +7124,10 @@ describe('ReactDOMFizzServer', () => {
71037124
// It surfaced in two different suspense boundaries.
71047125
__DEV__
71057126
? [
7106-
'The server did not finish this Suspense boundary: replay error',
7107-
'The server did not finish this Suspense boundary: replay error',
7127+
'Switched to client rendering because the server rendering aborted due to:\n\n' +
7128+
'replay error',
7129+
'Switched to client rendering because the server rendering aborted due to:\n\n' +
7130+
'replay error',
71087131
]
71097132
: [
71107133
'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
@@ -7230,7 +7253,10 @@ describe('ReactDOMFizzServer', () => {
72307253

72317254
expect(recoverableErrors).toEqual(
72327255
__DEV__
7233-
? ['server error']
7256+
? [
7257+
'Switched to client rendering because the server rendering errored:\n\n' +
7258+
'server error',
7259+
]
72347260
: [
72357261
'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
72367262
],

packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,10 +1292,12 @@ describe('ReactDOMServerHydration', () => {
12921292
}
12931293

12941294
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1295-
[
1296-
"Caught [The server did not finish this Suspense boundary: The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToPipeableStream" which supports Suspense on the server]",
1297-
]
1298-
`);
1295+
[
1296+
"Caught [Switched to client rendering because the server rendering aborted due to:
1297+
1298+
The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToPipeableStream" which supports Suspense on the server]",
1299+
]
1300+
`);
12991301
});
13001302

13011303
// @gate __DEV__
@@ -1318,10 +1320,12 @@ describe('ReactDOMServerHydration', () => {
13181320
}
13191321

13201322
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1321-
[
1322-
"Caught [The server did not finish this Suspense boundary: The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToPipeableStream" which supports Suspense on the server]",
1323-
]
1324-
`);
1323+
[
1324+
"Caught [Switched to client rendering because the server rendering aborted due to:
1325+
1326+
The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToPipeableStream" which supports Suspense on the server]",
1327+
]
1328+
`);
13251329
});
13261330
});
13271331

packages/react-reconciler/src/ReactFiberBeginWork.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2659,18 +2659,18 @@ function updateDehydratedSuspenseComponent(
26592659
// TODO: Figure out a better signal than encoding a magic digest value.
26602660
if (!enablePostpone || digest !== 'POSTPONE') {
26612661
let error;
2662-
if (message) {
2662+
if (__DEV__ && message) {
26632663
// eslint-disable-next-line react-internal/prod-error-codes
26642664
error = new Error(message);
26652665
} else {
26662666
error = new Error(
26672667
'The server could not finish this Suspense boundary, likely ' +
2668-
'due to an error during server rendering. Switched to ' +
2669-
'client rendering.',
2668+
'due to an error during server rendering. ' +
2669+
'Switched to client rendering.',
26702670
);
26712671
}
26722672
// Replace the stack with the server stack
2673-
error.stack = stack || '';
2673+
error.stack = (__DEV__ && stack) || '';
26742674
(error: any).digest = digest;
26752675
capturedValue = createCapturedValueFromError(
26762676
error,

packages/react-server/src/ReactFizzServer.js

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,7 @@ function encodeErrorForBoundary(
820820
digest: ?string,
821821
error: mixed,
822822
thrownInfo: ThrownInfo,
823+
wasAborted: boolean,
823824
) {
824825
boundary.errorDigest = digest;
825826
if (__DEV__) {
@@ -838,8 +839,10 @@ function encodeErrorForBoundary(
838839
message = String(error);
839840
stack = null;
840841
}
841-
842-
boundary.errorMessage = message;
842+
const prefix = wasAborted
843+
? 'Switched to client rendering because the server rendering aborted due to:\n\n'
844+
: 'Switched to client rendering because the server rendering errored:\n\n';
845+
boundary.errorMessage = prefix + message;
843846
boundary.errorStack = stack;
844847
boundary.errorComponentStack = thrownInfo.componentStack;
845848
}
@@ -1021,7 +1024,7 @@ function renderSuspenseBoundary(
10211024
} else {
10221025
errorDigest = logRecoverableError(request, error, thrownInfo);
10231026
}
1024-
encodeErrorForBoundary(newBoundary, errorDigest, error, thrownInfo);
1027+
encodeErrorForBoundary(newBoundary, errorDigest, error, thrownInfo, false);
10251028

10261029
untrackBoundary(request, newBoundary);
10271030

@@ -1165,7 +1168,13 @@ function replaySuspenseBoundary(
11651168
} else {
11661169
errorDigest = logRecoverableError(request, error, thrownInfo);
11671170
}
1168-
encodeErrorForBoundary(resumedBoundary, errorDigest, error, thrownInfo);
1171+
encodeErrorForBoundary(
1172+
resumedBoundary,
1173+
errorDigest,
1174+
error,
1175+
thrownInfo,
1176+
false,
1177+
);
11691178

11701179
task.replay.pendingTasks--;
11711180

@@ -2962,6 +2971,7 @@ function erroredReplay(
29622971
error,
29632972
errorDigest,
29642973
errorInfo,
2974+
false,
29652975
);
29662976
}
29672977

@@ -2992,7 +3002,7 @@ function erroredTask(
29923002
boundary.pendingTasks--;
29933003
if (boundary.status !== CLIENT_RENDERED) {
29943004
boundary.status = CLIENT_RENDERED;
2995-
encodeErrorForBoundary(boundary, errorDigest, error, errorInfo);
3005+
encodeErrorForBoundary(boundary, errorDigest, error, errorInfo, false);
29963006
untrackBoundary(request, boundary);
29973007

29983008
// Regardless of what happens next, this boundary won't be displayed,
@@ -3032,24 +3042,21 @@ function abortRemainingSuspenseBoundary(
30323042
error: mixed,
30333043
errorDigest: ?string,
30343044
errorInfo: ThrownInfo,
3045+
wasAborted: boolean,
30353046
): void {
30363047
const resumedBoundary = createSuspenseBoundary(request, new Set());
30373048
resumedBoundary.parentFlushed = true;
30383049
// We restore the same id of this boundary as was used during prerender.
30393050
resumedBoundary.rootSegmentID = rootSegmentID;
30403051

30413052
resumedBoundary.status = CLIENT_RENDERED;
3042-
let errorMessage = error;
3043-
if (__DEV__) {
3044-
const errorPrefix = 'The server did not finish this Suspense boundary: ';
3045-
if (error && typeof error.message === 'string') {
3046-
errorMessage = errorPrefix + error.message;
3047-
} else {
3048-
// eslint-disable-next-line react-internal/safe-string-coercion
3049-
errorMessage = errorPrefix + String(error);
3050-
}
3051-
}
3052-
encodeErrorForBoundary(resumedBoundary, errorDigest, errorMessage, errorInfo);
3053+
encodeErrorForBoundary(
3054+
resumedBoundary,
3055+
errorDigest,
3056+
error,
3057+
errorInfo,
3058+
wasAborted,
3059+
);
30533060

30543061
if (resumedBoundary.parentFlushed) {
30553062
request.clientRenderedBoundaries.push(resumedBoundary);
@@ -3064,6 +3071,7 @@ function abortRemainingReplayNodes(
30643071
error: mixed,
30653072
errorDigest: ?string,
30663073
errorInfo: ThrownInfo,
3074+
aborted: boolean,
30673075
): void {
30683076
for (let i = 0; i < nodes.length; i++) {
30693077
const node = nodes[i];
@@ -3076,6 +3084,7 @@ function abortRemainingReplayNodes(
30763084
error,
30773085
errorDigest,
30783086
errorInfo,
3087+
aborted,
30793088
);
30803089
} else {
30813090
const boundaryNode: ReplaySuspenseBoundary = node;
@@ -3086,6 +3095,7 @@ function abortRemainingReplayNodes(
30863095
error,
30873096
errorDigest,
30883097
errorInfo,
3098+
aborted,
30893099
);
30903100
}
30913101
}
@@ -3102,7 +3112,7 @@ function abortRemainingReplayNodes(
31023112
);
31033113
} else if (boundary.status !== CLIENT_RENDERED) {
31043114
boundary.status = CLIENT_RENDERED;
3105-
encodeErrorForBoundary(boundary, errorDigest, error, errorInfo);
3115+
encodeErrorForBoundary(boundary, errorDigest, error, errorInfo, aborted);
31063116
if (boundary.parentFlushed) {
31073117
request.clientRenderedBoundaries.push(boundary);
31083118
}
@@ -3178,6 +3188,7 @@ function abortTask(task: Task, request: Request, error: mixed): void {
31783188
error,
31793189
errorDigest,
31803190
errorInfo,
3191+
true,
31813192
);
31823193
}
31833194
request.pendingRootTasks--;
@@ -3207,18 +3218,7 @@ function abortTask(task: Task, request: Request, error: mixed): void {
32073218
} else {
32083219
errorDigest = logRecoverableError(request, error, errorInfo);
32093220
}
3210-
let errorMessage = error;
3211-
if (__DEV__) {
3212-
const errorPrefix =
3213-
'The server did not finish this Suspense boundary: ';
3214-
if (error && typeof error.message === 'string') {
3215-
errorMessage = errorPrefix + error.message;
3216-
} else {
3217-
// eslint-disable-next-line react-internal/safe-string-coercion
3218-
errorMessage = errorPrefix + String(error);
3219-
}
3220-
}
3221-
encodeErrorForBoundary(boundary, errorDigest, errorMessage, errorInfo);
3221+
encodeErrorForBoundary(boundary, errorDigest, error, errorInfo, true);
32223222

32233223
untrackBoundary(request, boundary);
32243224

0 commit comments

Comments
 (0)