@samitouri / QOS-React-2 / commits / 3db98c9177

[Fizz] Track Current debugTask and run it for onError Callbacks (#30182)

Stacked on #30174. This tracks the current debugTask on the Task so that when an error is thrown we can use it to run the `onError` (and `onShellError` and `onFatalError`) callbacks within the Context of that task. Ideally it would be associated with the error object but neither console.error [nor reportError](https://crbug.com/350426235) reports this as the async stack so we have to manually restore it. That way when you inspect Fizz using node `--inspect` we show the right async stack. <img width="616" alt="Screenshot 2024-07-01 at 10 52 29 PM" src="https://github.com/facebook/react/assets/63648/db68133e-124e-4509-8241-c67160db94fc"> This is equivalent to how we track the task that created the parent server component or the Fiber: https://github.com/facebook/react/blob/6d2a97a7113dfac2ad45067001b7e49a98718324/packages/react-reconciler/src/ReactChildFiber.js#L1985 Then use them when invoking the error callbacks: https://github.com/facebook/react/blob/6d2a97a7113dfac2ad45067001b7e49a98718324/packages/react-reconciler/src/ReactFiberThrow.js#L104-L108 --------- Co-authored-by: Sebastian Silbermann <silbermann.sebastian@gmail.com>

Sebastian Markbåge committed Jul 2, 2024 at 19:46 UTC 3db98c917701d59f62cf1fbe45cbf01b0b61c704
1 file changed +178 -80
packages/react-server/src/ReactFizzServer.js
+178 -80
@@ -250,6 +250,7 @@ type RenderTask = {
250 thenableState: null | ThenableState,
251 isFallback: boolean, // whether this task is rendering inside a fallback tree
252 legacyContext: LegacyContext, // the current legacy context that this task is executing in
253 + debugTask: null | ConsoleTask, // DEV only
254 // DON'T ANY MORE FIELDS. We at 16 already which otherwise requires converting to a constructor.
255 // Consider splitting into multiple objects or consolidating some fields.
256 };
@@ -279,6 +280,7 @@ type ReplayTask = {
280 thenableState: null | ThenableState,
281 isFallback: boolean, // whether this task is rendering inside a fallback tree
282 legacyContext: LegacyContext, // the current legacy context that this task is executing in
283 + debugTask: null | ConsoleTask, // DEV only
284 // DON'T ANY MORE FIELDS. We at 16 already which otherwise requires converting to a constructor.
285 // Consider splitting into multiple objects or consolidating some fields.
286 };
@@ -468,6 +470,7 @@ function RequestInstance(
470 null,
471 false,
472 emptyContextObject,
473 + null,
474 );
475 pingedTasks.push(rootTask);
476 }
@@ -610,6 +613,7 @@ export function resumeRequest(
613 null,
614 false,
615 emptyContextObject,
616 + null,
617 );
618 pingedTasks.push(rootTask);
619 return request;
@@ -636,6 +640,7 @@ export function resumeRequest(
640 null,
641 false,
642 emptyContextObject,
643 + null,
644 );
645 pingedTasks.push(rootTask);
646 return request;
@@ -704,6 +709,7 @@ function createRenderTask(
709 componentStack: null | ComponentStackNode,
710 isFallback: boolean,
711 legacyContext: LegacyContext,
712 + debugTask: null | ConsoleTask,
713 ): RenderTask {
714 request.allPendingTasks++;
715 if (blockedBoundary === null) {
@@ -731,6 +737,9 @@ function createRenderTask(
737 if (!disableLegacyContext) {
738 task.legacyContext = legacyContext;
739 }
740 + if (__DEV__ && enableOwnerStacks) {
741 + task.debugTask = debugTask;
742 + }
743 abortSet.add(task);
744 return task;
745 }
@@ -751,6 +760,7 @@ function createReplayTask(
760 componentStack: null | ComponentStackNode,
761 isFallback: boolean,
762 legacyContext: LegacyContext,
763 + debugTask: null | ConsoleTask,
764 ): ReplayTask {
765 request.allPendingTasks++;
766 if (blockedBoundary === null) {
@@ -779,6 +789,9 @@ function createReplayTask(
789 if (!disableLegacyContext) {
790 task.legacyContext = legacyContext;
791 }
792 + if (__DEV__ && enableOwnerStacks) {
793 + task.debugTask = debugTask;
794 + }
795 abortSet.add(task);
796 return task;
797 }
@@ -887,40 +900,44 @@ function createClassComponentStack(
900 type,
901 };
902 }
890 -function createServerComponentStack(
903 +function pushServerComponentStack(
904 task: Task,
905 debugInfo: void | null | ReactDebugInfo,
893 -): null | ComponentStackNode {
906 +): void {
907 + if (!__DEV__) {
908 + // eslint-disable-next-line react-internal/prod-error-codes
909 + throw new Error(
910 + 'pushServerComponentStack should never be called in production. This is a bug in React.',
911 + );
912 + }
913 // Build a Server Component parent stack from the debugInfo.
895 - if (__DEV__) {
896 - let node = task.componentStack;
897 - if (debugInfo != null) {
898 - const stack: ReactDebugInfo = debugInfo;
899 - for (let i = 0; i < stack.length; i++) {
900 - const componentInfo: ReactComponentInfo = (stack[i]: any);
901 - if (typeof componentInfo.name !== 'string') {
902 - continue;
903 - }
904 - let name = componentInfo.name;
905 - const env = componentInfo.env;
906 - if (env) {
907 - name += ' (' + env + ')';
908 - }
909 - node = {
910 - tag: 3,
911 - parent: node,
912 - type: name,
913 - owner: componentInfo.owner,
914 - stack: componentInfo.stack,
915 - };
914 + if (debugInfo != null) {
915 + const stack: ReactDebugInfo = debugInfo;
916 + for (let i = 0; i < stack.length; i++) {
917 + const componentInfo: ReactComponentInfo = (stack[i]: any);
918 + if (typeof componentInfo.name !== 'string') {
919 + continue;
920 + }
921 + if (enableOwnerStacks && componentInfo.stack === undefined) {
922 + continue;
923 + }
924 + let name = componentInfo.name;
925 + const env = componentInfo.env;
926 + if (env) {
927 + name += ' (' + env + ')';
928 + }
929 + task.componentStack = {
930 + tag: 3,
931 + parent: task.componentStack,
932 + type: name,
933 + owner: componentInfo.owner,
934 + stack: componentInfo.stack,
935 + };
936 + if (enableOwnerStacks) {
937 + task.debugTask = (componentInfo.task: any);
938 }
939 }
918 - return node;
940 }
920 - // eslint-disable-next-line react-internal/prod-error-codes
921 - throw new Error(
922 - 'createServerComponentStack should never be called in production. This is a bug in React.',
923 - );
941 }
942
943 function createComponentStackFromType(
@@ -1000,20 +1017,31 @@ function logPostpone(
1017 request: Request,
1018 reason: string,
1019 postponeInfo: ThrownInfo,
1020 + debugTask: null | ConsoleTask,
1021 ): void {
1022 // If this callback errors, we intentionally let that error bubble up to become a fatal error
1023 // so that someone fixes the error reporting instead of hiding it.
1006 - request.onPostpone(reason, postponeInfo);
1024 + const onPostpone = request.onPostpone;
1025 + if (__DEV__ && enableOwnerStacks && debugTask) {
1026 + debugTask.run(onPostpone.bind(null, reason, postponeInfo));
1027 + } else {
1028 + onPostpone(reason, postponeInfo);
1029 + }
1030 }
1031
1032 function logRecoverableError(
1033 request: Request,
1034 error: any,
1035 errorInfo: ThrownInfo,
1036 + debugTask: null | ConsoleTask,
1037 ): ?string {
1038 // If this callback errors, we intentionally let that error bubble up to become a fatal error
1039 // so that someone fixes the error reporting instead of hiding it.
1016 - const errorDigest = request.onError(error, errorInfo);
1040 + const onError = request.onError;
1041 + const errorDigest =
1042 + __DEV__ && enableOwnerStacks && debugTask
1043 + ? debugTask.run(onError.bind(null, error, errorInfo))
1044 + : onError(error, errorInfo);
1045 if (errorDigest != null && typeof errorDigest !== 'string') {
1046 // We used to throw here but since this gets called from a variety of unprotected places it
1047 // seems better to just warn and discard the returned value.
@@ -1028,14 +1056,24 @@ function logRecoverableError(
1056 return errorDigest;
1057 }
1058
1031 -function fatalError(request: Request, error: mixed): void {
1059 +function fatalError(
1060 + request: Request,
1061 + error: mixed,
1062 + errorInfo: ThrownInfo,
1063 + debugTask: null | ConsoleTask,
1064 +): void {
1065 // This is called outside error handling code such as if the root errors outside
1066 // a suspense boundary or if the root suspense boundary's fallback errors.
1067 // It's also called if React itself or its host configs errors.
1068 const onShellError = request.onShellError;
1036 - onShellError(error);
1069 const onFatalError = request.onFatalError;
1038 - onFatalError(error);
1070 + if (__DEV__ && enableOwnerStacks && debugTask) {
1071 + debugTask.run(onShellError.bind(null, error));
1072 + debugTask.run(onFatalError.bind(null, error));
1073 + } else {
1074 + onShellError(error);
1075 + onFatalError(error);
1076 + }
1077 if (request.destination !== null) {
1078 request.status = CLOSED;
1079 closeWithError(request.destination, error);
@@ -1168,11 +1206,21 @@ function renderSuspenseBoundary(
1206 error.$$typeof === REACT_POSTPONE_TYPE
1207 ) {
1208 const postponeInstance: Postpone = (error: any);
1171 - logPostpone(request, postponeInstance.message, thrownInfo);
1209 + logPostpone(
1210 + request,
1211 + postponeInstance.message,
1212 + thrownInfo,
1213 + __DEV__ && enableOwnerStacks ? task.debugTask : null,
1214 + );
1215 // TODO: Figure out a better signal than a magic digest value.
1216 errorDigest = 'POSTPONE';
1217 } else {
1175 - errorDigest = logRecoverableError(request, error, thrownInfo);
1218 + errorDigest = logRecoverableError(
1219 + request,
1220 + error,
1221 + thrownInfo,
1222 + __DEV__ && enableOwnerStacks ? task.debugTask : null,
1223 + );
1224 }
1225 encodeErrorForBoundary(newBoundary, errorDigest, error, thrownInfo, false);
1226
@@ -1231,6 +1279,7 @@ function renderSuspenseBoundary(
1279 suspenseComponentStack,
1280 true,
1281 !disableLegacyContext ? task.legacyContext : emptyContextObject,
1282 + __DEV__ && enableOwnerStacks ? task.debugTask : null,
1283 );
1284 // TODO: This should be queued at a separate lower priority queue so that we only work
1285 // on preparing fallbacks if we don't have any more main content to task on.
@@ -1314,11 +1363,21 @@ function replaySuspenseBoundary(
1363 error.$$typeof === REACT_POSTPONE_TYPE
1364 ) {
1365 const postponeInstance: Postpone = (error: any);
1317 - logPostpone(request, postponeInstance.message, thrownInfo);
1366 + logPostpone(
1367 + request,
1368 + postponeInstance.message,
1369 + thrownInfo,
1370 + __DEV__ && enableOwnerStacks ? task.debugTask : null,
1371 + );
1372 // TODO: Figure out a better signal than a magic digest value.
1373 errorDigest = 'POSTPONE';
1374 } else {
1321 - errorDigest = logRecoverableError(request, error, thrownInfo);
1375 + errorDigest = logRecoverableError(
1376 + request,
1377 + error,
1378 + thrownInfo,
1379 + __DEV__ && enableOwnerStacks ? task.debugTask : null,
1380 + );
1381 }
1382 encodeErrorForBoundary(
1383 resumedBoundary,
@@ -1371,6 +1430,7 @@ function replaySuspenseBoundary(
1430 suspenseComponentStack,
1431 true,
1432 !disableLegacyContext ? task.legacyContext : emptyContextObject,
1433 + __DEV__ && enableOwnerStacks ? task.debugTask : null,
1434 );
1435 // TODO: This should be queued at a separate lower priority queue so that we only work
1436 // on preparing fallbacks if we don't have any more main content to task on.
@@ -2378,6 +2438,7 @@ function replayElement(
2438 thrownInfo,
2439 childNodes,
2440 childSlots,
2441 + __DEV__ && enableOwnerStacks ? task.debugTask : null,
2442 );
2443 }
2444 task.replay = replay;
@@ -2570,12 +2631,17 @@ function renderNodeDestructive(
2631 const owner = __DEV__ ? element._owner : null;
2632 const stack = __DEV__ && enableOwnerStacks ? element._debugStack : null;
2633
2634 + let previousDebugTask: null | ConsoleTask = null;
2635 const previousComponentStack = task.componentStack;
2636 + let debugTask: null | ConsoleTask;
2637 if (__DEV__) {
2575 - task.componentStack = createServerComponentStack(
2576 - task,
2577 - element._debugInfo,
2578 - );
2638 + if (enableOwnerStacks) {
2639 + previousDebugTask = task.debugTask;
2640 + }
2641 + pushServerComponentStack(task, element._debugInfo);
2642 + if (enableOwnerStacks) {
2643 + task.debugTask = debugTask = element._debugTask;
2644 + }
2645 }
2646
2647 const name = getComponentNameFromType(type);
@@ -2583,8 +2649,6 @@ function renderNodeDestructive(
2649 key == null ? (childIndex === -1 ? 0 : childIndex) : key;
2650 const keyPath = [task.keyPath, name, keyOrIndex];
2651 if (task.replay !== null) {
2586 - const debugTask: null | ConsoleTask =
2587 - __DEV__ && enableOwnerStacks ? element._debugTask : null;
2652 if (debugTask) {
2653 debugTask.run(
2654 replayElement.bind(
@@ -2623,8 +2687,6 @@ function renderNodeDestructive(
2687 // prelude and skip it during the replay.
2688 } else {
2689 // We're doing a plain render.
2626 - const debugTask: null | ConsoleTask =
2627 - __DEV__ && enableOwnerStacks ? element._debugTask : null;
2690 if (debugTask) {
2691 debugTask.run(
2692 renderElement.bind(
@@ -2654,6 +2716,9 @@ function renderNodeDestructive(
2716 }
2717 if (__DEV__) {
2718 task.componentStack = previousComponentStack;
2719 + if (enableOwnerStacks) {
2720 + task.debugTask = previousDebugTask;
2721 + }
2722 }
2723 return;
2724 }
@@ -2665,11 +2730,12 @@ function renderNodeDestructive(
2730 case REACT_LAZY_TYPE: {
2731 const lazyNode: LazyComponentType<any, any> = (node: any);
2732 const previousComponentStack = task.componentStack;
2733 + let previousDebugTask = null;
2734 if (__DEV__) {
2669 - task.componentStack = createServerComponentStack(
2670 - task,
2671 - lazyNode._debugInfo,
2672 - );
2735 + if (enableOwnerStacks) {
2736 + previousDebugTask = task.debugTask;
2737 + }
2738 + pushServerComponentStack(task, lazyNode._debugInfo);
2739 }
2740 if (!__DEV__ || task.componentStack === previousComponentStack) {
2741 // TODO: Do we really need this stack frame? We don't on the client.
@@ -2692,6 +2758,9 @@ function renderNodeDestructive(
2758 // We restore the stack before rendering the resolved node because once the Lazy
2759 // has resolved any future errors
2760 task.componentStack = previousComponentStack;
2761 + if (__DEV__ && enableOwnerStacks) {
2762 + task.debugTask = previousDebugTask;
2763 + }
2764
2765 // Now we render the resolved node
2766 renderNodeDestructive(request, task, resolvedNode, childIndex);
@@ -2812,10 +2881,7 @@ function renderNodeDestructive(
2881 const thenable: Thenable<ReactNodeList> = (maybeUsable: any);
2882 const previousComponentStack = task.componentStack;
2883 if (__DEV__) {
2815 - task.componentStack = createServerComponentStack(
2816 - task,
2817 - thenable._debugInfo,
2818 - );
2884 + pushServerComponentStack(task, thenable._debugInfo);
2885 }
2886 const result = renderNodeDestructive(
2887 request,
@@ -2947,6 +3013,7 @@ function replayFragment(
3013 thrownInfo,
3014 childNodes,
3015 childSlots,
3016 + __DEV__ && enableOwnerStacks ? task.debugTask : null,
3017 );
3018 }
3019 task.replay = replay;
@@ -3058,13 +3125,14 @@ function renderChildrenArray(
3125 ): void {
3126 const prevKeyPath = task.keyPath;
3127 const previousComponentStack = task.componentStack;
3128 + let previousDebugTask = null;
3129 if (__DEV__) {
3130 + if (enableOwnerStacks) {
3131 + previousDebugTask = task.debugTask;
3132 + }
3133 // We read debugInfo from task.node instead of children because it might have been an
3134 // unwrapped iterable so we read from the original node.
3064 - task.componentStack = createServerComponentStack(
3065 - task,
3066 - (task.node: any)._debugInfo,
3067 - );
3135 + pushServerComponentStack(task, (task.node: any)._debugInfo);
3136 }
3137 if (childIndex !== -1) {
3138 task.keyPath = [task.keyPath, 'Fragment', childIndex];
@@ -3079,6 +3147,9 @@ function renderChildrenArray(
3147 task.keyPath = prevKeyPath;
3148 if (__DEV__) {
3149 task.componentStack = previousComponentStack;
3150 + if (enableOwnerStacks) {
3151 + task.debugTask = previousDebugTask;
3152 + }
3153 }
3154 return;
3155 }
@@ -3112,6 +3183,9 @@ function renderChildrenArray(
3183 task.keyPath = prevKeyPath;
3184 if (__DEV__) {
3185 task.componentStack = previousComponentStack;
3186 + if (enableOwnerStacks) {
3187 + task.debugTask = previousDebugTask;
3188 + }
3189 }
3190 return;
3191 }
@@ -3134,6 +3208,9 @@ function renderChildrenArray(
3208 task.keyPath = prevKeyPath;
3209 if (__DEV__) {
3210 task.componentStack = previousComponentStack;
3211 + if (enableOwnerStacks) {
3212 + task.debugTask = previousDebugTask;
3213 + }
3214 }
3215 }
3216
@@ -3327,7 +3404,12 @@ function injectPostponedHole(
3404 reason: string,
3405 thrownInfo: ThrownInfo,
3406 ): Segment {
3330 - logPostpone(request, reason, thrownInfo);
3407 + logPostpone(
3408 + request,
3409 + reason,
3410 + thrownInfo,
3411 + __DEV__ && enableOwnerStacks ? task.debugTask : null,
3412 + );
3413 // Something suspended, we'll need to create a new segment and resolve it later.
3414 const segment = task.blockedSegment;
3415 const insertionIndex = segment.chunks.length;
@@ -3371,6 +3453,7 @@ function spawnNewSuspendedReplayTask(
3453 task.componentStack !== null ? task.componentStack.parent : null,
3454 task.isFallback,
3455 !disableLegacyContext ? task.legacyContext : emptyContextObject,
3456 + __DEV__ && enableOwnerStacks ? task.debugTask : null,
3457 );
3458
3459 const ping = newTask.ping;
@@ -3417,6 +3500,7 @@ function spawnNewSuspendedRenderTask(
3500 task.componentStack !== null ? task.componentStack.parent : null,
3501 task.isFallback,
3502 !disableLegacyContext ? task.legacyContext : emptyContextObject,
3503 + __DEV__ && enableOwnerStacks ? task.debugTask : null,
3504 );
3505
3506 const ping = newTask.ping;
@@ -3609,6 +3693,7 @@ function erroredReplay(
3693 errorInfo: ThrownInfo,
3694 replayNodes: ReplayNode[],
3695 resumeSlots: ResumeSlots,
3696 + debugTask: null | ConsoleTask,
3697 ): void {
3698 // Erroring during a replay doesn't actually cause an error by itself because
3699 // that component has already rendered. What causes the error is the resumable
@@ -3625,11 +3710,11 @@ function erroredReplay(
3710 error.$$typeof === REACT_POSTPONE_TYPE
3711 ) {
3712 const postponeInstance: Postpone = (error: any);
3628 - logPostpone(request, postponeInstance.message, errorInfo);
3713 + logPostpone(request, postponeInstance.message, errorInfo, debugTask);
3714 // TODO: Figure out a better signal than a magic digest value.
3715 errorDigest = 'POSTPONE';
3716 } else {
3632 - errorDigest = logRecoverableError(request, error, errorInfo);
3717 + errorDigest = logRecoverableError(request, error, errorInfo, debugTask);
3718 }
3719 abortRemainingReplayNodes(
3720 request,
@@ -3648,6 +3733,7 @@ function erroredTask(
3733 boundary: Root | SuspenseBoundary,
3734 error: mixed,
3735 errorInfo: ThrownInfo,
3736 + debugTask: null | ConsoleTask,
3737 ) {
3738 // Report the error to a global handler.
3739 let errorDigest;
@@ -3658,14 +3744,14 @@ function erroredTask(
3744 error.$$typeof === REACT_POSTPONE_TYPE
3745 ) {
3746 const postponeInstance: Postpone = (error: any);
3661 - logPostpone(request, postponeInstance.message, errorInfo);
3747 + logPostpone(request, postponeInstance.message, errorInfo, debugTask);
3748 // TODO: Figure out a better signal than a magic digest value.
3749 errorDigest = 'POSTPONE';
3750 } else {
3665 - errorDigest = logRecoverableError(request, error, errorInfo);
3751 + errorDigest = logRecoverableError(request, error, errorInfo, debugTask);
3752 }
3753 if (boundary === null) {
3668 - fatalError(request, error);
3754 + fatalError(request, error, errorInfo, debugTask);
3755 } else {
3756 boundary.pendingTasks--;
3757 if (boundary.status !== CLIENT_RENDERED) {
@@ -3821,11 +3907,11 @@ function abortTask(task: Task, request: Request, error: mixed): void {
3907 'The render was aborted with postpone when the shell is incomplete. Reason: ' +
3908 postponeInstance.message,
3909 );
3824 - logRecoverableError(request, fatal, errorInfo);
3825 - fatalError(request, fatal);
3910 + logRecoverableError(request, fatal, errorInfo, null);
3911 + fatalError(request, fatal, errorInfo, null);
3912 } else {
3827 - logRecoverableError(request, error, errorInfo);
3828 - fatalError(request, error);
3913 + logRecoverableError(request, error, errorInfo, null);
3914 + fatalError(request, error, errorInfo, null);
3915 }
3916 return;
3917 } else {
@@ -3842,11 +3928,11 @@ function abortTask(task: Task, request: Request, error: mixed): void {
3928 error.$$typeof === REACT_POSTPONE_TYPE
3929 ) {
3930 const postponeInstance: Postpone = (error: any);
3845 - logPostpone(request, postponeInstance.message, errorInfo);
3931 + logPostpone(request, postponeInstance.message, errorInfo, null);
3932 // TODO: Figure out a better signal than a magic digest value.
3933 errorDigest = 'POSTPONE';
3934 } else {
3849 - errorDigest = logRecoverableError(request, error, errorInfo);
3935 + errorDigest = logRecoverableError(request, error, errorInfo, null);
3936 }
3937 abortRemainingReplayNodes(
3938 request,
@@ -3880,11 +3966,11 @@ function abortTask(task: Task, request: Request, error: mixed): void {
3966 error.$$typeof === REACT_POSTPONE_TYPE
3967 ) {
3968 const postponeInstance: Postpone = (error: any);
3883 - logPostpone(request, postponeInstance.message, errorInfo);
3969 + logPostpone(request, postponeInstance.message, errorInfo, null);
3970 // TODO: Figure out a better signal than a magic digest value.
3971 errorDigest = 'POSTPONE';
3972 } else {
3887 - errorDigest = logRecoverableError(request, error, errorInfo);
3973 + errorDigest = logRecoverableError(request, error, errorInfo, null);
3974 }
3975 encodeErrorForBoundary(boundary, errorDigest, error, errorInfo, true);
3976
@@ -3922,7 +4008,7 @@ function safelyEmitEarlyPreloads(
4008 } catch (error) {
4009 // We assume preloads are optimistic and thus non-fatal if errored.
4010 const errorInfo: ThrownInfo = {};
3925 - logRecoverableError(request, error, errorInfo);
4011 + logRecoverableError(request, error, errorInfo, null);
4012 }
4013 }
4014
@@ -4164,7 +4250,12 @@ function retryRenderTask(
4250 const postponeInstance: Postpone = (x: any);
4251
4252 const postponeInfo = getThrownInfo(task.componentStack);
4167 - logPostpone(request, postponeInstance.message, postponeInfo);
4253 + logPostpone(
4254 + request,
4255 + postponeInstance.message,
4256 + postponeInfo,
4257 + __DEV__ && enableOwnerStacks ? task.debugTask : null,
4258 + );
4259 trackPostpone(request, trackedPostpones, task, segment);
4260 finishedTask(request, task.blockedBoundary, segment);
4261 return;
@@ -4174,7 +4265,13 @@ function retryRenderTask(
4265 const errorInfo = getThrownInfo(task.componentStack);
4266 task.abortSet.delete(task);
4267 segment.status = ERRORED;
4177 - erroredTask(request, task.blockedBoundary, x, errorInfo);
4268 + erroredTask(
4269 + request,
4270 + task.blockedBoundary,
4271 + x,
4272 + errorInfo,
4273 + __DEV__ && enableOwnerStacks ? task.debugTask : null,
4274 + );
4275 return;
4276 } finally {
4277 if (__DEV__) {
@@ -4253,6 +4350,7 @@ function retryReplayTask(request: Request, task: ReplayTask): void {
4350 errorInfo,
4351 task.replay.nodes,
4352 task.replay.slots,
4353 + __DEV__ && enableOwnerStacks ? task.debugTask : null,
4354 );
4355 request.pendingRootTasks--;
4356 if (request.pendingRootTasks === 0) {
@@ -4306,8 +4404,8 @@ export function performWork(request: Request): void {
4404 }
4405 } catch (error) {
4406 const errorInfo: ThrownInfo = {};
4309 - logRecoverableError(request, error, errorInfo);
4310 - fatalError(request, error);
4407 + logRecoverableError(request, error, errorInfo, null);
4408 + fatalError(request, error, errorInfo, null);
4409 } finally {
4410 setCurrentResumableState(prevResumableState);
4411 ReactSharedInternals.H = prevDispatcher;
@@ -4891,8 +4989,8 @@ export function startFlowing(request: Request, destination: Destination): void {
4989 flushCompletedQueues(request, destination);
4990 } catch (error) {
4991 const errorInfo: ThrownInfo = {};
4894 - logRecoverableError(request, error, errorInfo);
4895 - fatalError(request, error);
4992 + logRecoverableError(request, error, errorInfo, null);
4993 + fatalError(request, error, errorInfo, null);
4994 }
4995 }
4996
@@ -4917,8 +5015,8 @@ export function abort(request: Request, reason: mixed): void {
5015 }
5016 } catch (error) {
5017 const errorInfo: ThrownInfo = {};
4920 - logRecoverableError(request, error, errorInfo);
4921 - fatalError(request, error);
5018 + logRecoverableError(request, error, errorInfo, null);
5019 + fatalError(request, error, errorInfo, null);
5020 }
5021 }
5022