Skip to content

Commit b239394

Browse files
committed
[Fizz] Reset error component stack and fix error messages (#27456)
The way we collect component stacks right now are pretty fragile. We expect that we'll call captureBoundaryErrorDetailsDev whenever an error happens. That resets lastBoundaryErrorComponentStackDev to null but if we don't, it just lingers and we don't set it to anything new then which leaks the previous component stack into the next time we have an error. So we need to reset it in a bunch of places. This is still broken with erroredReplay because it has the inverse problem that abortRemainingReplayNodes can call captureBoundaryErrorDetailsDev more than one time. So the second boundary won't get a stack. We probably should try to figure out an alternative way to carry along the stack. Perhaps WeakMap keyed by the error object. This also fixes an issue where we weren't invoking the onShellReady event if we error a replay. That event is a bit weird for resuming because we're probably really supposed to just invoke it immediately if we have already flushed the shell in the prerender which is always atm. Right now, it gets invoked later than necessary because you could have a resumed hole ready before a sibling in the shell is ready and that's blocked. DiffTrain build for [0fba3ec](0fba3ec)
1 parent 4efc125 commit b239394

9 files changed

+316
-183
lines changed

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
ca237d6f0ab986e799f192224d3066f76d66b73b
1+
0fba3ecf73900a1b54ed6d3b0617462ac92d2fef

compiled/facebook-www/ReactART-dev.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ function _assertThisInitialized(self) {
6969
return self;
7070
}
7171

72-
var ReactVersion = "18.3.0-www-modern-73c8edac";
72+
var ReactVersion = "18.3.0-www-modern-55737203";
7373

7474
var LegacyRoot = 0;
7575
var ConcurrentRoot = 1;

compiled/facebook-www/ReactART-prod.modern.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9774,7 +9774,7 @@ var slice = Array.prototype.slice,
97749774
return null;
97759775
},
97769776
bundleType: 0,
9777-
version: "18.3.0-www-modern-e0184800",
9777+
version: "18.3.0-www-modern-f919825d",
97789778
rendererPackageName: "react-art"
97799779
};
97809780
var internals$jscomp$inline_1284 = {
@@ -9805,7 +9805,7 @@ var internals$jscomp$inline_1284 = {
98059805
scheduleRoot: null,
98069806
setRefreshHandler: null,
98079807
getCurrentFiber: null,
9808-
reconcilerVersion: "18.3.0-www-modern-e0184800"
9808+
reconcilerVersion: "18.3.0-www-modern-f919825d"
98099809
};
98109810
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
98119811
var hook$jscomp$inline_1285 = __REACT_DEVTOOLS_GLOBAL_HOOK__;

compiled/facebook-www/ReactDOMServer-dev.classic.js

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ if (__DEV__) {
1919
var React = require("react");
2020
var ReactDOM = require("react-dom");
2121

22-
var ReactVersion = "18.3.0-www-classic-bdb0331d";
22+
var ReactVersion = "18.3.0-www-classic-6d747e3d";
2323

2424
// This refers to a WWW module.
2525
var warningWWW = require("warning");
@@ -10649,8 +10649,7 @@ function replaySuspenseBoundary(
1064910649
} // TODO: This should be queued at a separate lower priority queue so that we only work
1065010650
// on preparing fallbacks if we don't have any more main content to task on.
1065110651

10652-
request.pingedTasks.push(suspendedFallbackTask); // TODO: Should this be in the finally?
10653-
10652+
request.pingedTasks.push(suspendedFallbackTask);
1065410653
popComponentStackInDEV(task);
1065510654
}
1065610655

@@ -11422,19 +11421,22 @@ function replayElement(
1142211421

1142311422
if (keyOrIndex !== node[1]) {
1142411423
continue;
11425-
} // Let's double check that the component name matches as a precaution.
11426-
11427-
if (name !== null && name !== node[0]) {
11428-
throw new Error(
11429-
'Expected to see a component of type "' +
11430-
name +
11431-
'" in this slot. ' +
11432-
"The tree doesn't match so React will fallback to client rendering."
11433-
);
1143411424
}
1143511425

1143611426
if (node.length === 4) {
1143711427
// Matched a replayable path.
11428+
// Let's double check that the component name matches as a precaution.
11429+
if (name !== null && name !== node[0]) {
11430+
throw new Error(
11431+
"Expected the resume to render <" +
11432+
node[0] +
11433+
"> in this slot but instead it rendered <" +
11434+
name +
11435+
">. " +
11436+
"The tree doesn't match so React will fallback to client rendering."
11437+
);
11438+
}
11439+
1143811440
var childNodes = node[2];
1143911441
var childSlots = node[3];
1144011442
task.replay = {
@@ -11485,8 +11487,13 @@ function replayElement(
1148511487
} else {
1148611488
// Let's double check that the component type matches.
1148711489
if (type !== REACT_SUSPENSE_TYPE) {
11490+
var expectedType = "Suspense";
1148811491
throw new Error(
11489-
"Expected to see a Suspense boundary in this slot. " +
11492+
"Expected the resume to render <" +
11493+
expectedType +
11494+
"> in this slot but instead it rendered <" +
11495+
(getComponentNameFromType(type) || "Unknown") +
11496+
">. " +
1149011497
"The tree doesn't match so React will fallback to client rendering."
1149111498
);
1149211499
} // Matched a replayable path.
@@ -11851,6 +11858,7 @@ function replayFragment(request, task, children, childIndex) {
1185111858
// in the original prerender. What's unable to complete is the child
1185211859
// replay nodes which might be Suspense boundaries which are able to
1185311860
// absorb the error and we can still continue with siblings.
11861+
// This is an error, stash the component stack if it is null.
1185411862

1185511863
erroredReplay(request, task.blockedBoundary, x, childNodes, childSlots);
1185611864
} finally {
@@ -12165,6 +12173,7 @@ function erroredTask(request, boundary, error) {
1216512173
}
1216612174

1216712175
if (boundary === null) {
12176+
lastBoundaryErrorComponentStackDev = null;
1216812177
fatalError(request, error);
1216912178
} else {
1217012179
boundary.pendingTasks--;
@@ -12185,6 +12194,8 @@ function erroredTask(request, boundary, error) {
1218512194
// We reuse the same queue for errors.
1218612195
request.clientRenderedBoundaries.push(boundary);
1218712196
}
12197+
} else {
12198+
lastBoundaryErrorComponentStackDev = null;
1218812199
}
1218912200
}
1219012201

@@ -12323,8 +12334,6 @@ function abortTask(task, request, error) {
1232312334
}
1232412335

1232512336
if (boundary === null) {
12326-
request.allPendingTasks--;
12327-
1232812337
if (request.status !== CLOSING && request.status !== CLOSED) {
1232912338
var replay = task.replay;
1233012339

@@ -12333,6 +12342,7 @@ function abortTask(task, request, error) {
1233312342
// the request;
1233412343
logRecoverableError(request, error);
1233512344
fatalError(request, error);
12345+
return;
1233612346
} else {
1233712347
// If the shell aborts during a replay, that's not a fatal error. Instead
1233812348
// we should be able to recover by client rendering all the root boundaries in
@@ -12350,6 +12360,14 @@ function abortTask(task, request, error) {
1235012360
errorDigest
1235112361
);
1235212362
}
12363+
12364+
request.pendingRootTasks--;
12365+
12366+
if (request.pendingRootTasks === 0) {
12367+
request.onShellError = noop;
12368+
var onShellReady = request.onShellReady;
12369+
onShellReady();
12370+
}
1235312371
}
1235412372
}
1235512373
} else {
@@ -12390,12 +12408,13 @@ function abortTask(task, request, error) {
1239012408
return abortTask(fallbackTask, request, error);
1239112409
});
1239212410
boundary.fallbackAbortableTasks.clear();
12393-
request.allPendingTasks--;
12411+
}
1239412412

12395-
if (request.allPendingTasks === 0) {
12396-
var onAllReady = request.onAllReady;
12397-
onAllReady();
12398-
}
12413+
request.allPendingTasks--;
12414+
12415+
if (request.allPendingTasks === 0) {
12416+
var onAllReady = request.onAllReady;
12417+
onAllReady();
1239912418
}
1240012419
}
1240112420

@@ -12678,6 +12697,14 @@ function retryReplayTask(request, task) {
1267812697
task.replay.nodes,
1267912698
task.replay.slots
1268012699
);
12700+
request.pendingRootTasks--;
12701+
12702+
if (request.pendingRootTasks === 0) {
12703+
request.onShellError = noop;
12704+
var onShellReady = request.onShellReady;
12705+
onShellReady();
12706+
}
12707+
1268112708
request.allPendingTasks--;
1268212709

1268312710
if (request.allPendingTasks === 0) {

compiled/facebook-www/ReactDOMServer-dev.modern.js

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ if (__DEV__) {
1919
var React = require("react");
2020
var ReactDOM = require("react-dom");
2121

22-
var ReactVersion = "18.3.0-www-modern-73c8edac";
22+
var ReactVersion = "18.3.0-www-modern-55737203";
2323

2424
// This refers to a WWW module.
2525
var warningWWW = require("warning");
@@ -10408,8 +10408,7 @@ function replaySuspenseBoundary(
1040810408
} // TODO: This should be queued at a separate lower priority queue so that we only work
1040910409
// on preparing fallbacks if we don't have any more main content to task on.
1041010410

10411-
request.pingedTasks.push(suspendedFallbackTask); // TODO: Should this be in the finally?
10412-
10411+
request.pingedTasks.push(suspendedFallbackTask);
1041310412
popComponentStackInDEV(task);
1041410413
}
1041510414

@@ -11170,19 +11169,22 @@ function replayElement(
1117011169

1117111170
if (keyOrIndex !== node[1]) {
1117211171
continue;
11173-
} // Let's double check that the component name matches as a precaution.
11174-
11175-
if (name !== null && name !== node[0]) {
11176-
throw new Error(
11177-
'Expected to see a component of type "' +
11178-
name +
11179-
'" in this slot. ' +
11180-
"The tree doesn't match so React will fallback to client rendering."
11181-
);
1118211172
}
1118311173

1118411174
if (node.length === 4) {
1118511175
// Matched a replayable path.
11176+
// Let's double check that the component name matches as a precaution.
11177+
if (name !== null && name !== node[0]) {
11178+
throw new Error(
11179+
"Expected the resume to render <" +
11180+
node[0] +
11181+
"> in this slot but instead it rendered <" +
11182+
name +
11183+
">. " +
11184+
"The tree doesn't match so React will fallback to client rendering."
11185+
);
11186+
}
11187+
1118611188
var childNodes = node[2];
1118711189
var childSlots = node[3];
1118811190
task.replay = {
@@ -11233,8 +11235,13 @@ function replayElement(
1123311235
} else {
1123411236
// Let's double check that the component type matches.
1123511237
if (type !== REACT_SUSPENSE_TYPE) {
11238+
var expectedType = "Suspense";
1123611239
throw new Error(
11237-
"Expected to see a Suspense boundary in this slot. " +
11240+
"Expected the resume to render <" +
11241+
expectedType +
11242+
"> in this slot but instead it rendered <" +
11243+
(getComponentNameFromType(type) || "Unknown") +
11244+
">. " +
1123811245
"The tree doesn't match so React will fallback to client rendering."
1123911246
);
1124011247
} // Matched a replayable path.
@@ -11599,6 +11606,7 @@ function replayFragment(request, task, children, childIndex) {
1159911606
// in the original prerender. What's unable to complete is the child
1160011607
// replay nodes which might be Suspense boundaries which are able to
1160111608
// absorb the error and we can still continue with siblings.
11609+
// This is an error, stash the component stack if it is null.
1160211610

1160311611
erroredReplay(request, task.blockedBoundary, x, childNodes, childSlots);
1160411612
} finally {
@@ -11913,6 +11921,7 @@ function erroredTask(request, boundary, error) {
1191311921
}
1191411922

1191511923
if (boundary === null) {
11924+
lastBoundaryErrorComponentStackDev = null;
1191611925
fatalError(request, error);
1191711926
} else {
1191811927
boundary.pendingTasks--;
@@ -11933,6 +11942,8 @@ function erroredTask(request, boundary, error) {
1193311942
// We reuse the same queue for errors.
1193411943
request.clientRenderedBoundaries.push(boundary);
1193511944
}
11945+
} else {
11946+
lastBoundaryErrorComponentStackDev = null;
1193611947
}
1193711948
}
1193811949

@@ -12071,8 +12082,6 @@ function abortTask(task, request, error) {
1207112082
}
1207212083

1207312084
if (boundary === null) {
12074-
request.allPendingTasks--;
12075-
1207612085
if (request.status !== CLOSING && request.status !== CLOSED) {
1207712086
var replay = task.replay;
1207812087

@@ -12081,6 +12090,7 @@ function abortTask(task, request, error) {
1208112090
// the request;
1208212091
logRecoverableError(request, error);
1208312092
fatalError(request, error);
12093+
return;
1208412094
} else {
1208512095
// If the shell aborts during a replay, that's not a fatal error. Instead
1208612096
// we should be able to recover by client rendering all the root boundaries in
@@ -12098,6 +12108,14 @@ function abortTask(task, request, error) {
1209812108
errorDigest
1209912109
);
1210012110
}
12111+
12112+
request.pendingRootTasks--;
12113+
12114+
if (request.pendingRootTasks === 0) {
12115+
request.onShellError = noop;
12116+
var onShellReady = request.onShellReady;
12117+
onShellReady();
12118+
}
1210112119
}
1210212120
}
1210312121
} else {
@@ -12138,12 +12156,13 @@ function abortTask(task, request, error) {
1213812156
return abortTask(fallbackTask, request, error);
1213912157
});
1214012158
boundary.fallbackAbortableTasks.clear();
12141-
request.allPendingTasks--;
12159+
}
1214212160

12143-
if (request.allPendingTasks === 0) {
12144-
var onAllReady = request.onAllReady;
12145-
onAllReady();
12146-
}
12161+
request.allPendingTasks--;
12162+
12163+
if (request.allPendingTasks === 0) {
12164+
var onAllReady = request.onAllReady;
12165+
onAllReady();
1214712166
}
1214812167
}
1214912168

@@ -12426,6 +12445,14 @@ function retryReplayTask(request, task) {
1242612445
task.replay.nodes,
1242712446
task.replay.slots
1242812447
);
12448+
request.pendingRootTasks--;
12449+
12450+
if (request.pendingRootTasks === 0) {
12451+
request.onShellError = noop;
12452+
var onShellReady = request.onShellReady;
12453+
onShellReady();
12454+
}
12455+
1242912456
request.allPendingTasks--;
1243012457

1243112458
if (request.allPendingTasks === 0) {

0 commit comments

Comments
 (0)