Skip to content

Commit 2de2c20

Browse files
committed
Set the current fiber to the source of the error during error reporting (#29044)
This lets us expose the component stack to the error reporting that happens here as `console.error` patching. Now if you just call `console.error` in the error handlers it'll get the component stack added to the end by React DevTools. However, unfortunately this happens a little too late so the Fiber will be disconnected with its `.return` pointer set to null already. So it'll be too late to extract a parent component stack from but you can at least get the stack from source to error boundary. To work around this I manually add the parent component stack in our default handlers when owner stacks are off. We could potentially fix this but you can also just include it yourself if you're calling `console.error` and it's not a problem for owner stacks. This is not a problem for owner stacks because we'll still have those and so for those just calling `console.error` just works. However, the main feature is that by letting React add them, we can switch to using native error stacks when available. DiffTrain build for [2e540e2](2e540e2)
1 parent 89d5de9 commit 2de2c20

12 files changed

+353
-75
lines changed

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2e3e6a9b1cc97ec91248be74565e7ccbf6946067
1+
2e540e22b2b4038a278b2875306976b016fb31a9

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ function _assertThisInitialized(self) {
6060
return self;
6161
}
6262

63-
var ReactVersion = '19.0.0-www-classic-53ce7a77';
63+
var ReactVersion = '19.0.0-www-classic-15c5a6dc';
6464

6565
var LegacyRoot = 0;
6666
var ConcurrentRoot = 1;
@@ -12491,7 +12491,11 @@ function createRootErrorUpdate(root, errorInfo, lane) {
1249112491
};
1249212492

1249312493
update.callback = function () {
12494+
var prevFiber = getCurrentFiber(); // should just be the root
12495+
12496+
setCurrentDebugFiberInDEV(errorInfo.source);
1249412497
logUncaughtError(root, errorInfo);
12498+
setCurrentDebugFiberInDEV(prevFiber);
1249512499
};
1249612500

1249712501
return update;
@@ -12518,7 +12522,11 @@ function initializeClassErrorUpdate(update, root, fiber, errorInfo) {
1251812522
markFailedErrorBoundaryForHotReloading(fiber);
1251912523
}
1252012524

12525+
var prevFiber = getCurrentFiber(); // should be the error boundary
12526+
12527+
setCurrentDebugFiberInDEV(errorInfo.source);
1252112528
logCaughtError(root, fiber, errorInfo);
12529+
setCurrentDebugFiberInDEV(prevFiber);
1252212530
};
1252312531
}
1252412532

@@ -12531,7 +12539,11 @@ function initializeClassErrorUpdate(update, root, fiber, errorInfo) {
1253112539
markFailedErrorBoundaryForHotReloading(fiber);
1253212540
}
1253312541

12542+
var prevFiber = getCurrentFiber(); // should be the error boundary
12543+
12544+
setCurrentDebugFiberInDEV(errorInfo.source);
1253412545
logCaughtError(root, fiber, errorInfo);
12546+
setCurrentDebugFiberInDEV(prevFiber);
1253512547

1253612548
if (typeof getDerivedStateFromError !== 'function') {
1253712549
// To preserve the preexisting retry behavior of error boundaries,
@@ -24159,7 +24171,9 @@ function commitRootImpl(root, recoverableErrors, transitions, didIncludeRenderPh
2415924171
for (var i = 0; i < recoverableErrors.length; i++) {
2416024172
var recoverableError = recoverableErrors[i];
2416124173
var errorInfo = makeErrorInfo(recoverableError.stack);
24174+
setCurrentDebugFiberInDEV(recoverableError.source);
2416224175
onRecoverableError(recoverableError.value, errorInfo);
24176+
resetCurrentDebugFiberInDEV();
2416324177
}
2416424178
} // If the passive effects are the result of a discrete render, flush them
2416524179
// synchronously at the end of the current task so that the result is

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ function _assertThisInitialized(self) {
6060
return self;
6161
}
6262

63-
var ReactVersion = '19.0.0-www-modern-d3cf6c2c';
63+
var ReactVersion = '19.0.0-www-modern-1bbec70f';
6464

6565
var LegacyRoot = 0;
6666
var ConcurrentRoot = 1;
@@ -12256,7 +12256,11 @@ function createRootErrorUpdate(root, errorInfo, lane) {
1225612256
};
1225712257

1225812258
update.callback = function () {
12259+
var prevFiber = getCurrentFiber(); // should just be the root
12260+
12261+
setCurrentDebugFiberInDEV(errorInfo.source);
1225912262
logUncaughtError(root, errorInfo);
12263+
setCurrentDebugFiberInDEV(prevFiber);
1226012264
};
1226112265

1226212266
return update;
@@ -12283,7 +12287,11 @@ function initializeClassErrorUpdate(update, root, fiber, errorInfo) {
1228312287
markFailedErrorBoundaryForHotReloading(fiber);
1228412288
}
1228512289

12290+
var prevFiber = getCurrentFiber(); // should be the error boundary
12291+
12292+
setCurrentDebugFiberInDEV(errorInfo.source);
1228612293
logCaughtError(root, fiber, errorInfo);
12294+
setCurrentDebugFiberInDEV(prevFiber);
1228712295
};
1228812296
}
1228912297

@@ -12296,7 +12304,11 @@ function initializeClassErrorUpdate(update, root, fiber, errorInfo) {
1229612304
markFailedErrorBoundaryForHotReloading(fiber);
1229712305
}
1229812306

12307+
var prevFiber = getCurrentFiber(); // should be the error boundary
12308+
12309+
setCurrentDebugFiberInDEV(errorInfo.source);
1229912310
logCaughtError(root, fiber, errorInfo);
12311+
setCurrentDebugFiberInDEV(prevFiber);
1230012312

1230112313
if (typeof getDerivedStateFromError !== 'function') {
1230212314
// To preserve the preexisting retry behavior of error boundaries,
@@ -23513,7 +23525,9 @@ function commitRootImpl(root, recoverableErrors, transitions, didIncludeRenderPh
2351323525
for (var i = 0; i < recoverableErrors.length; i++) {
2351423526
var recoverableError = recoverableErrors[i];
2351523527
var errorInfo = makeErrorInfo(recoverableError.stack);
23528+
setCurrentDebugFiberInDEV(recoverableError.source);
2351623529
onRecoverableError(recoverableError.value, errorInfo);
23530+
resetCurrentDebugFiberInDEV();
2351723531
}
2351823532
} // If the passive effects are the result of a discrete render, flush them
2351923533
// synchronously at the end of the current task so that the result is

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

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16244,22 +16244,39 @@ function defaultOnUncaughtError(error, errorInfo) {
1624416244
reportGlobalError(error);
1624516245

1624616246
{
16247-
var componentStack = errorInfo.componentStack != null ? errorInfo.componentStack : '';
16248-
var componentNameMessage = componentName ? "An error occurred in the <" + componentName + "> component:" : 'An error occurred in one of your React components:';
16249-
console['warn']('%s\n%s\n\n%s', componentNameMessage, componentStack || '', 'Consider adding an error boundary to your tree to customize error handling behavior.\n' + 'Visit https://react.dev/link/error-boundaries to learn more about error boundaries.');
16247+
var componentNameMessage = componentName ? "An error occurred in the <" + componentName + "> component." : 'An error occurred in one of your React components.';
16248+
var errorBoundaryMessage = 'Consider adding an error boundary to your tree to customize error handling behavior.\n' + 'Visit https://react.dev/link/error-boundaries to learn more about error boundaries.';
16249+
16250+
{
16251+
// The current Fiber is disconnected at this point which means that console printing
16252+
// cannot add a component stack since it terminates at the deletion node. This is not
16253+
// a problem for owner stacks which are not disconnected but for the parent component
16254+
// stacks we need to use the snapshot we've previously extracted.
16255+
var componentStack = errorInfo.componentStack != null ? errorInfo.componentStack : ''; // Don't transform to our wrapper
16256+
16257+
console['warn']('%s\n\n%s\n%s', componentNameMessage, errorBoundaryMessage, componentStack);
16258+
}
1625016259
}
1625116260
}
16252-
function defaultOnCaughtError(error, errorInfo) {
16261+
function defaultOnCaughtError(error$1, errorInfo) {
1625316262
// Overriding this can silence these warnings e.g. for tests.
1625416263
// See https://github.com/facebook/react/pull/13384
1625516264
// Caught by error boundary
1625616265
{
16257-
var componentStack = errorInfo.componentStack != null ? errorInfo.componentStack : '';
16258-
var componentNameMessage = componentName ? "The above error occurred in the <" + componentName + "> component:" : 'The above error occurred in one of your React components:'; // In development, we provide our own message which includes the component stack
16266+
var componentNameMessage = componentName ? "The above error occurred in the <" + componentName + "> component." : 'The above error occurred in one of your React components.'; // In development, we provide our own message which includes the component stack
1625916267
// in addition to the error.
16260-
// Don't transform to our wrapper
1626116268

16262-
console['error']('%o\n\n%s\n%s\n\n%s', error, componentNameMessage, componentStack, "React will try to recreate this component tree from scratch " + ("using the error boundary you provided, " + (errorBoundaryName || 'Anonymous') + "."));
16269+
var recreateMessage = "React will try to recreate this component tree from scratch " + ("using the error boundary you provided, " + (errorBoundaryName || 'Anonymous') + ".");
16270+
16271+
{
16272+
// The current Fiber is disconnected at this point which means that console printing
16273+
// cannot add a component stack since it terminates at the deletion node. This is not
16274+
// a problem for owner stacks which are not disconnected but for the parent component
16275+
// stacks we need to use the snapshot we've previously extracted.
16276+
var componentStack = errorInfo.componentStack != null ? errorInfo.componentStack : ''; // Don't transform to our wrapper
16277+
16278+
console['error']('%o\n\n%s\n\n%s\n%s', error$1, componentNameMessage, recreateMessage, componentStack);
16279+
}
1626316280
}
1626416281
}
1626516282
function defaultOnRecoverableError(error, errorInfo) {
@@ -16331,7 +16348,11 @@ function createRootErrorUpdate(root, errorInfo, lane) {
1633116348
};
1633216349

1633316350
update.callback = function () {
16351+
var prevFiber = getCurrentFiber(); // should just be the root
16352+
16353+
setCurrentDebugFiberInDEV(errorInfo.source);
1633416354
logUncaughtError(root, errorInfo);
16355+
setCurrentDebugFiberInDEV(prevFiber);
1633516356
};
1633616357

1633716358
return update;
@@ -16358,7 +16379,11 @@ function initializeClassErrorUpdate(update, root, fiber, errorInfo) {
1635816379
markFailedErrorBoundaryForHotReloading(fiber);
1635916380
}
1636016381

16382+
var prevFiber = getCurrentFiber(); // should be the error boundary
16383+
16384+
setCurrentDebugFiberInDEV(errorInfo.source);
1636116385
logCaughtError(root, fiber, errorInfo);
16386+
setCurrentDebugFiberInDEV(prevFiber);
1636216387
};
1636316388
}
1636416389

@@ -16371,7 +16396,11 @@ function initializeClassErrorUpdate(update, root, fiber, errorInfo) {
1637116396
markFailedErrorBoundaryForHotReloading(fiber);
1637216397
}
1637316398

16399+
var prevFiber = getCurrentFiber(); // should be the error boundary
16400+
16401+
setCurrentDebugFiberInDEV(errorInfo.source);
1637416402
logCaughtError(root, fiber, errorInfo);
16403+
setCurrentDebugFiberInDEV(prevFiber);
1637516404

1637616405
if (typeof getDerivedStateFromError !== 'function') {
1637716406
// To preserve the preexisting retry behavior of error boundaries,
@@ -28862,7 +28891,9 @@ function commitRootImpl(root, recoverableErrors, transitions, didIncludeRenderPh
2886228891
for (var i = 0; i < recoverableErrors.length; i++) {
2886328892
var recoverableError = recoverableErrors[i];
2886428893
var errorInfo = makeErrorInfo(recoverableError.stack);
28894+
setCurrentDebugFiberInDEV(recoverableError.source);
2886528895
onRecoverableError(recoverableError.value, errorInfo);
28896+
resetCurrentDebugFiberInDEV();
2886628897
}
2886728898
} // If the passive effects are the result of a discrete render, flush them
2886828899
// synchronously at the end of the current task so that the result is
@@ -30852,7 +30883,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition
3085230883
return root;
3085330884
}
3085430885

30855-
var ReactVersion = '19.0.0-www-classic-d3c0ad7c';
30886+
var ReactVersion = '19.0.0-www-classic-6551c42e';
3085630887

3085730888
function createPortal$1(children, containerInfo, // TODO: figure out the API for cross-renderer implementation.
3085830889
implementation) {

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

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15961,22 +15961,39 @@ function defaultOnUncaughtError(error, errorInfo) {
1596115961
reportGlobalError(error);
1596215962

1596315963
{
15964-
var componentStack = errorInfo.componentStack != null ? errorInfo.componentStack : '';
15965-
var componentNameMessage = componentName ? "An error occurred in the <" + componentName + "> component:" : 'An error occurred in one of your React components:';
15966-
console['warn']('%s\n%s\n\n%s', componentNameMessage, componentStack || '', 'Consider adding an error boundary to your tree to customize error handling behavior.\n' + 'Visit https://react.dev/link/error-boundaries to learn more about error boundaries.');
15964+
var componentNameMessage = componentName ? "An error occurred in the <" + componentName + "> component." : 'An error occurred in one of your React components.';
15965+
var errorBoundaryMessage = 'Consider adding an error boundary to your tree to customize error handling behavior.\n' + 'Visit https://react.dev/link/error-boundaries to learn more about error boundaries.';
15966+
15967+
{
15968+
// The current Fiber is disconnected at this point which means that console printing
15969+
// cannot add a component stack since it terminates at the deletion node. This is not
15970+
// a problem for owner stacks which are not disconnected but for the parent component
15971+
// stacks we need to use the snapshot we've previously extracted.
15972+
var componentStack = errorInfo.componentStack != null ? errorInfo.componentStack : ''; // Don't transform to our wrapper
15973+
15974+
console['warn']('%s\n\n%s\n%s', componentNameMessage, errorBoundaryMessage, componentStack);
15975+
}
1596715976
}
1596815977
}
15969-
function defaultOnCaughtError(error, errorInfo) {
15978+
function defaultOnCaughtError(error$1, errorInfo) {
1597015979
// Overriding this can silence these warnings e.g. for tests.
1597115980
// See https://github.com/facebook/react/pull/13384
1597215981
// Caught by error boundary
1597315982
{
15974-
var componentStack = errorInfo.componentStack != null ? errorInfo.componentStack : '';
15975-
var componentNameMessage = componentName ? "The above error occurred in the <" + componentName + "> component:" : 'The above error occurred in one of your React components:'; // In development, we provide our own message which includes the component stack
15983+
var componentNameMessage = componentName ? "The above error occurred in the <" + componentName + "> component." : 'The above error occurred in one of your React components.'; // In development, we provide our own message which includes the component stack
1597615984
// in addition to the error.
15977-
// Don't transform to our wrapper
1597815985

15979-
console['error']('%o\n\n%s\n%s\n\n%s', error, componentNameMessage, componentStack, "React will try to recreate this component tree from scratch " + ("using the error boundary you provided, " + (errorBoundaryName || 'Anonymous') + "."));
15986+
var recreateMessage = "React will try to recreate this component tree from scratch " + ("using the error boundary you provided, " + (errorBoundaryName || 'Anonymous') + ".");
15987+
15988+
{
15989+
// The current Fiber is disconnected at this point which means that console printing
15990+
// cannot add a component stack since it terminates at the deletion node. This is not
15991+
// a problem for owner stacks which are not disconnected but for the parent component
15992+
// stacks we need to use the snapshot we've previously extracted.
15993+
var componentStack = errorInfo.componentStack != null ? errorInfo.componentStack : ''; // Don't transform to our wrapper
15994+
15995+
console['error']('%o\n\n%s\n\n%s\n%s', error$1, componentNameMessage, recreateMessage, componentStack);
15996+
}
1598015997
}
1598115998
}
1598215999
function defaultOnRecoverableError(error, errorInfo) {
@@ -16048,7 +16065,11 @@ function createRootErrorUpdate(root, errorInfo, lane) {
1604816065
};
1604916066

1605016067
update.callback = function () {
16068+
var prevFiber = getCurrentFiber(); // should just be the root
16069+
16070+
setCurrentDebugFiberInDEV(errorInfo.source);
1605116071
logUncaughtError(root, errorInfo);
16072+
setCurrentDebugFiberInDEV(prevFiber);
1605216073
};
1605316074

1605416075
return update;
@@ -16075,7 +16096,11 @@ function initializeClassErrorUpdate(update, root, fiber, errorInfo) {
1607516096
markFailedErrorBoundaryForHotReloading(fiber);
1607616097
}
1607716098

16099+
var prevFiber = getCurrentFiber(); // should be the error boundary
16100+
16101+
setCurrentDebugFiberInDEV(errorInfo.source);
1607816102
logCaughtError(root, fiber, errorInfo);
16103+
setCurrentDebugFiberInDEV(prevFiber);
1607916104
};
1608016105
}
1608116106

@@ -16088,7 +16113,11 @@ function initializeClassErrorUpdate(update, root, fiber, errorInfo) {
1608816113
markFailedErrorBoundaryForHotReloading(fiber);
1608916114
}
1609016115

16116+
var prevFiber = getCurrentFiber(); // should be the error boundary
16117+
16118+
setCurrentDebugFiberInDEV(errorInfo.source);
1609116119
logCaughtError(root, fiber, errorInfo);
16120+
setCurrentDebugFiberInDEV(prevFiber);
1609216121

1609316122
if (typeof getDerivedStateFromError !== 'function') {
1609416123
// To preserve the preexisting retry behavior of error boundaries,
@@ -28113,7 +28142,9 @@ function commitRootImpl(root, recoverableErrors, transitions, didIncludeRenderPh
2811328142
for (var i = 0; i < recoverableErrors.length; i++) {
2811428143
var recoverableError = recoverableErrors[i];
2811528144
var errorInfo = makeErrorInfo(recoverableError.stack);
28145+
setCurrentDebugFiberInDEV(recoverableError.source);
2811628146
onRecoverableError(recoverableError.value, errorInfo);
28147+
resetCurrentDebugFiberInDEV();
2811728148
}
2811828149
} // If the passive effects are the result of a discrete render, flush them
2811928150
// synchronously at the end of the current task so that the result is
@@ -30027,7 +30058,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition
3002730058
return root;
3002830059
}
3002930060

30030-
var ReactVersion = '19.0.0-www-modern-533bc87e';
30061+
var ReactVersion = '19.0.0-www-modern-c6c71924';
3003130062

3003230063
function createPortal$1(children, containerInfo, // TODO: figure out the API for cross-renderer implementation.
3003330064
implementation) {

0 commit comments

Comments
 (0)