426
implicitSlot: boolean, // true if the root server component of this sequence had a null key
427
thenableState: ThenableState | null,
428
environmentName: string, // DEV-only. Used to track if the environment for this task changed.
429
+ debugOwner: null | ReactComponentInfo, // DEV-only
430
+ debugStack: null | string, // DEV-only
431
+ debugTask: null | ConsoleTask, // DEV-only
432
};
433
434
interface Reference {}
580
: environmentName;
581
this.didWarnForKey = null;
582
}
580
- const rootTask = createTask(this, model, null, false, abortSet);
583
+ const rootTask = createTask(
584
+ this,
585
+ model,
586
+ null,
587
+ false,
588
+ abortSet,
589
+ null,
590
+ null,
591
+ null,
592
+ );
593
pingedTasks.push(rootTask);
594
}
595
636
task.keyPath, // the server component sequence continues through Promise-as-a-child.
637
task.implicitSlot,
638
request.abortableTasks,
639
+ __DEV__ && enableOwnerStacks ? task.debugOwner : null,
640
+ __DEV__ && enableOwnerStacks ? task.debugStack : null,
641
+ __DEV__ && enableOwnerStacks ? task.debugTask : null,
642
);
643
if (__DEV__) {
644
// If this came from Flight, forward any debug info into this new row.
664
(x: any).$$typeof === REACT_POSTPONE_TYPE
665
) {
666
const postponeInstance: Postpone = (x: any);
652
- logPostpone(request, postponeInstance.message);
667
+ logPostpone(request, postponeInstance.message, newTask);
668
emitPostponeChunk(request, newTask.id, postponeInstance);
669
} else {
655
- const digest = logRecoverableError(request, x);
670
+ const digest = logRecoverableError(request, x, null);
671
emitErrorChunk(request, newTask.id, digest, x);
672
}
673
return newTask.id;
723
(reason: any).$$typeof === REACT_POSTPONE_TYPE
724
) {
725
const postponeInstance: Postpone = (reason: any);
711
- logPostpone(request, postponeInstance.message);
726
+ logPostpone(request, postponeInstance.message, newTask);
727
emitPostponeChunk(request, newTask.id, postponeInstance);
728
} else {
729
newTask.status = ERRORED;
715
- const digest = logRecoverableError(request, reason);
730
+ const digest = logRecoverableError(request, reason, newTask);
731
emitErrorChunk(request, newTask.id, digest, reason);
732
}
733
request.abortableTasks.delete(newTask);
768
task.keyPath,
769
task.implicitSlot,
770
request.abortableTasks,
771
+ __DEV__ && enableOwnerStacks ? task.debugOwner : null,
772
+ __DEV__ && enableOwnerStacks ? task.debugStack : null,
773
+ __DEV__ && enableOwnerStacks ? task.debugTask : null,
774
);
775
request.abortableTasks.delete(streamTask);
776
819
(reason: any).$$typeof === REACT_POSTPONE_TYPE
820
) {
821
const postponeInstance: Postpone = (reason: any);
804
- logPostpone(request, postponeInstance.message);
822
+ logPostpone(request, postponeInstance.message, streamTask);
823
emitPostponeChunk(request, streamTask.id, postponeInstance);
824
} else {
807
- const digest = logRecoverableError(request, reason);
825
+ const digest = logRecoverableError(request, reason, streamTask);
826
emitErrorChunk(request, streamTask.id, digest, reason);
827
}
828
enqueueFlush(request);
867
task.keyPath,
868
task.implicitSlot,
869
request.abortableTasks,
870
+ __DEV__ && enableOwnerStacks ? task.debugOwner : null,
871
+ __DEV__ && enableOwnerStacks ? task.debugStack : null,
872
+ __DEV__ && enableOwnerStacks ? task.debugTask : null,
873
);
874
request.abortableTasks.delete(streamTask);
875
951
(reason: any).$$typeof === REACT_POSTPONE_TYPE
952
) {
953
const postponeInstance: Postpone = (reason: any);
933
- logPostpone(request, postponeInstance.message);
954
+ logPostpone(request, postponeInstance.message, streamTask);
955
emitPostponeChunk(request, streamTask.id, postponeInstance);
956
} else {
936
- const digest = logRecoverableError(request, reason);
957
+ const digest = logRecoverableError(request, reason, streamTask);
958
emitErrorChunk(request, streamTask.id, digest, reason);
959
}
960
enqueueFlush(request);
1098
return init(payload);
1099
}
1100
1101
+function callWithDebugContextInDEV<A, T>(
1102
+ task: Task,
1103
+ callback: A => T,
1104
+ arg: A,
1105
+): T {
1106
+ // We don't have a Server Component instance associated with this callback and
1107
+ // the nearest context is likely a Client Component being serialized. We create
1108
+ // a fake owner during this callback so we can get the stack trace from it.
1109
+ // This also gets sent to the client as the owner for the replaying log.
1110
+ const componentDebugInfo: ReactComponentInfo = {
1111
+ env: task.environmentName,
1112
+ owner: task.debugOwner,
1113
+ };
1114
+ if (enableOwnerStacks) {
1115
+ // $FlowFixMe[cannot-write]
1116
+ componentDebugInfo.stack = task.debugStack;
1117
+ }
1118
+ const debugTask = task.debugTask;
1119
+ // We don't need the async component storage context here so we only set the
1120
+ // synchronous tracking of owner.
1121
+ setCurrentOwner(componentDebugInfo);
1122
+ try {
1123
+ if (enableOwnerStacks && debugTask) {
1124
+ return debugTask.run(callback.bind(null, arg));
1125
+ }
1126
+ return callback(arg);
1127
+ } finally {
1128
+ setCurrentOwner(null);
1129
+ }
1130
+}
1131
+
1132
function renderFunctionComponent<Props>(
1133
request: Request,
1134
task: Task,
1135
key: null | string,
1136
Component: (p: Props, arg: void) => any,
1137
props: Props,
1086
- owner: null | ReactComponentInfo, // DEV-only
1087
- stack: null | string, // DEV-only
1088
- debugTask: null | ConsoleTask, // DEV-only
1138
validated: number, // DEV-only
1139
): ReactJSONValue {
1140
// Reset the task's thenable state before continuing, so that if a later
1166
componentDebugInfo = ({
1167
name: componentName,
1168
env: componentEnv,
1120
- owner: owner,
1169
+ owner: task.debugOwner,
1170
}: ReactComponentInfo);
1171
if (enableOwnerStacks) {
1172
// $FlowFixMe[cannot-write]
1124
- componentDebugInfo.stack = stack;
1173
+ componentDebugInfo.stack = task.debugStack;
1174
}
1175
// We outline this model eagerly so that we can refer to by reference as an owner.
1176
// If we had a smarter way to dedupe we might not have to do this if there ends up
1187
key,
1188
validated,
1189
componentDebugInfo,
1141
- debugTask,
1190
+ task.debugTask,
1191
);
1192
}
1193
}
1196
Component,
1197
props,
1198
componentDebugInfo,
1150
- debugTask,
1199
+ task.debugTask,
1200
);
1201
} else {
1202
prepareToUseHooksForComponent(prevThenableState, null);
1271
Object.prototype.toString.call(iterableChild) ===
1272
'[object Generator]';
1273
if (!isGeneratorComponent) {
1225
- console.error(
1226
- 'Returning an Iterator from a Server Component is not supported ' +
1227
- 'since it cannot be looped over more than once. ',
1228
- );
1274
+ callWithDebugContextInDEV(task, () => {
1275
+ console.error(
1276
+ 'Returning an Iterator from a Server Component is not supported ' +
1277
+ 'since it cannot be looped over more than once. ',
1278
+ );
1279
+ });
1280
}
1281
}
1282
}
1310
Object.prototype.toString.call(iterableChild) ===
1311
'[object AsyncGenerator]';
1312
if (!isGeneratorComponent) {
1262
- console.error(
1263
- 'Returning an AsyncIterator from a Server Component is not supported ' +
1264
- 'since it cannot be looped over more than once. ',
1265
- );
1313
+ callWithDebugContextInDEV(task, () => {
1314
+ console.error(
1315
+ 'Returning an AsyncIterator from a Server Component is not supported ' +
1316
+ 'since it cannot be looped over more than once. ',
1317
+ );
1318
+ });
1319
}
1320
}
1321
}
1542
type: any,
1543
key: null | string,
1544
props: any,
1492
- owner: null | ReactComponentInfo, // DEV-only
1493
- stack: null | string, // DEV-only
1545
validated: number, // DEV-only
1546
): ReactJSONValue {
1547
// We prepend the terminal client element that actually gets serialized with
1554
}
1555
const element = __DEV__
1556
? enableOwnerStacks
1506
- ? [REACT_ELEMENT_TYPE, type, key, props, owner, stack, validated]
1507
- : [REACT_ELEMENT_TYPE, type, key, props, owner]
1557
+ ? [
1558
+ REACT_ELEMENT_TYPE,
1559
+ type,
1560
+ key,
1561
+ props,
1562
+ task.debugOwner,
1563
+ task.debugStack,
1564
+ validated,
1565
+ ]
1566
+ : [REACT_ELEMENT_TYPE, type, key, props, task.debugOwner]
1567
: [REACT_ELEMENT_TYPE, type, key, props];
1568
if (task.implicitSlot && key !== null) {
1569
// The root Server Component had no key so it was in an implicit slot.
1590
task.keyPath, // unlike outlineModel this one carries along context
1591
task.implicitSlot,
1592
request.abortableTasks,
1593
+ __DEV__ && enableOwnerStacks ? task.debugOwner : null,
1594
+ __DEV__ && enableOwnerStacks ? task.debugStack : null,
1595
+ __DEV__ && enableOwnerStacks ? task.debugTask : null,
1596
);
1597
1598
retryTask(request, newTask);
1613
key: null | string,
1614
ref: mixed,
1615
props: any,
1554
- owner: null | ReactComponentInfo, // DEV only
1555
- stack: null | string, // DEV only
1556
- debugTask: null | ConsoleTask, // DEV only
1616
validated: number, // DEV only
1617
): ReactJSONValue {
1618
if (ref !== null && ref !== undefined) {
1637
!isOpaqueTemporaryReference(type)
1638
) {
1639
// This is a Server Component.
1581
- return renderFunctionComponent(
1582
- request,
1583
- task,
1584
- key,
1585
- type,
1586
- props,
1587
- owner,
1588
- stack,
1589
- debugTask,
1590
- validated,
1591
- );
1640
+ return renderFunctionComponent(request, task, key, type, props, validated);
1641
} else if (type === REACT_FRAGMENT_TYPE && key === null) {
1642
// For key-less fragments, we add a small optimization to avoid serializing
1643
// it as a wrapper.
1682
key,
1683
ref,
1684
props,
1636
- owner,
1637
- stack,
1638
- debugTask,
1685
validated,
1686
);
1687
}
1692
key,
1693
type.render,
1694
props,
1649
- owner,
1650
- stack,
1651
- debugTask,
1695
validated,
1696
);
1697
}
1703
key,
1704
ref,
1705
props,
1663
- owner,
1664
- stack,
1665
- debugTask,
1706
validated,
1707
);
1708
}
1720
// We don't know if the client will support it or not. This might error on the
1721
// client or error during serialization but the stack will point back to the
1722
// server.
1683
- return renderClientElement(task, type, key, props, owner, stack, validated);
1723
+ return renderClientElement(task, type, key, props, validated);
1724
}
1725
1726
function pingTask(request: Request, task: Task): void {
1738
keyPath: null | string,
1739
implicitSlot: boolean,
1740
abortSet: Set<Task>,
1741
+ debugOwner: null | ReactComponentInfo, // DEV-only
1742
+ debugStack: null | string, // DEV-only
1743
+ debugTask: null | ConsoleTask, // DEV-only
1744
): Task {
1745
request.pendingChunks++;
1746
const id = request.nextChunkId++;
1778
originalValue !== value &&
1779
!(originalValue instanceof Date)
1780
) {
1738
- if (objectName(originalValue) !== 'Object') {
1739
- const jsxParentType = jsxChildrenParents.get(parent);
1740
- if (typeof jsxParentType === 'string') {
1741
- console.error(
1742
- '%s objects cannot be rendered as text children. Try formatting it using toString().%s',
1743
- objectName(originalValue),
1744
- describeObjectForErrorMessage(parent, parentPropertyName),
1745
- );
1781
+ // Call with the server component as the currently rendering component
1782
+ // for context.
1783
+ callWithDebugContextInDEV(task, () => {
1784
+ if (objectName(originalValue) !== 'Object') {
1785
+ const jsxParentType = jsxChildrenParents.get(parent);
1786
+ if (typeof jsxParentType === 'string') {
1787
+ console.error(
1788
+ '%s objects cannot be rendered as text children. Try formatting it using toString().%s',
1789
+ objectName(originalValue),
1790
+ describeObjectForErrorMessage(parent, parentPropertyName),
1791
+ );
1792
+ } else {
1793
+ console.error(
1794
+ 'Only plain objects can be passed to Client Components from Server Components. ' +
1795
+ '%s objects are not supported.%s',
1796
+ objectName(originalValue),
1797
+ describeObjectForErrorMessage(parent, parentPropertyName),
1798
+ );
1799
+ }
1800
} else {
1801
console.error(
1802
'Only plain objects can be passed to Client Components from Server Components. ' +
1749
- '%s objects are not supported.%s',
1750
- objectName(originalValue),
1803
+ 'Objects with toJSON methods are not supported. Convert it manually ' +
1804
+ 'to a simple value before passing it to props.%s',
1805
describeObjectForErrorMessage(parent, parentPropertyName),
1806
);
1807
}
1754
- } else {
1755
- console.error(
1756
- 'Only plain objects can be passed to Client Components from Server Components. ' +
1757
- 'Objects with toJSON methods are not supported. Convert it manually ' +
1758
- 'to a simple value before passing it to props.%s',
1759
- describeObjectForErrorMessage(parent, parentPropertyName),
1760
- );
1761
- }
1808
+ });
1809
}
1810
}
1811
return renderModel(request, task, parent, parentPropertyName, value);
1812
},
1813
thenableState: null,
1767
- }: Omit<Task, 'environmentName'>): any);
1814
+ }: Omit<
1815
+ Task,
1816
+ 'environmentName' | 'debugOwner' | 'debugStack' | 'debugTask',
1817
+ >): any);
1818
if (__DEV__) {
1819
task.environmentName = request.environmentName();
1820
+ if (enableOwnerStacks) {
1821
+ task.debugOwner = debugOwner;
1822
+ task.debugStack = debugStack;
1823
+ task.debugTask = debugTask;
1824
+ }
1825
}
1826
abortSet.add(task);
1827
return task;
1939
} catch (x) {
1940
request.pendingChunks++;
1941
const errorId = request.nextChunkId++;
1887
- const digest = logRecoverableError(request, x);
1942
+ const digest = logRecoverableError(request, x, null);
1943
emitErrorChunk(request, errorId, digest, x);
1944
return serializeByValueID(errorId);
1945
}
1952
null, // The way we use outlining is for reusing an object.
1953
false, // It makes no sense for that use case to be contextual.
1954
request.abortableTasks,
1955
+ null, // TODO: Currently we don't associate any debug information with
1956
+ null, // this object on the server. If it ends up erroring, it won't
1957
+ null, // have any context on the server but can on the client.
1958
);
1959
retryTask(request, newTask);
1960
return newTask.id;
2048
null,
2049
false,
2050
request.abortableTasks,
2051
+ null, // TODO: Currently we don't associate any debug information with
2052
+ null, // this object on the server. If it ends up erroring, it won't
2053
+ null, // have any context on the server but can on the client.
2054
);
2055
2056
const reader = blob.stream().getReader();
2080
}
2081
aborted = true;
2082
request.abortListeners.delete(error);
2022
- const digest = logRecoverableError(request, reason);
2083
+ const digest = logRecoverableError(request, reason, newTask);
2084
emitErrorChunk(request, newTask.id, digest, reason);
2085
request.abortableTasks.delete(newTask);
2086
enqueueFlush(request);
2159
task.keyPath,
2160
task.implicitSlot,
2161
request.abortableTasks,
2162
+ __DEV__ && enableOwnerStacks ? task.debugOwner : null,
2163
+ __DEV__ && enableOwnerStacks ? task.debugStack : null,
2164
+ __DEV__ && enableOwnerStacks ? task.debugTask : null,
2165
);
2166
const ping = newTask.ping;
2167
(x: any).then(ping, ping);
2182
const postponeInstance: Postpone = (x: any);
2183
request.pendingChunks++;
2184
const postponeId = request.nextChunkId++;
2121
- logPostpone(request, postponeInstance.message);
2185
+ logPostpone(request, postponeInstance.message, task);
2186
emitPostponeChunk(request, postponeId, postponeInstance);
2187
2188
// Restore the context. We assume that this will be restored by the inner
2214
// Something errored. We'll still send everything we have up until this point.
2215
request.pendingChunks++;
2216
const errorId = request.nextChunkId++;
2153
- const digest = logRecoverableError(request, x);
2217
+ const digest = logRecoverableError(request, x, task);
2218
emitErrorChunk(request, errorId, digest, x);
2219
if (wasReactNode) {
2220
// We'll replace this element with a lazy reference that throws on the client
2316
}
2317
2318
// Attempt to render the Server Component.
2319
+
2320
+ if (__DEV__) {
2321
+ task.debugOwner = element._owner;
2322
+ if (enableOwnerStacks) {
2323
+ task.debugStack =
2324
+ !element._debugStack || typeof element._debugStack === 'string'
2325
+ ? element._debugStack
2326
+ : filterDebugStack(element._debugStack);
2327
+ task.debugTask = element._debugTask;
2328
+ }
2329
+ // TODO: Pop this. Since we currently don't have a point where we can pop the stack
2330
+ // this debug information will be used for errors inside sibling properties that
2331
+ // are not elements. Leading to the wrong attribution on the server. We could fix
2332
+ // that if we switch to a proper stack instead of JSON.stringify's trampoline.
2333
+ // Attribution on the client is still correct since it has a pop.
2334
+ }
2335
+
2336
const newChild = renderElement(
2337
request,
2338
task,
2341
element.key,
2342
ref,
2343
props,
2263
- __DEV__ ? element._owner : null,
2264
- __DEV__ && enableOwnerStacks
2265
- ? !element._debugStack || typeof element._debugStack === 'string'
2266
- ? element._debugStack
2267
- : filterDebugStack(element._debugStack)
2268
- : null,
2269
- __DEV__ && enableOwnerStacks ? element._debugTask : null,
2344
__DEV__ && enableOwnerStacks ? element._store.validated : 0,
2345
);
2346
if (
2647
}
2648
2649
if (objectName(value) !== 'Object') {
2576
- console.error(
2577
- 'Only plain objects can be passed to Client Components from Server Components. ' +
2578
- '%s objects are not supported.%s',
2579
- objectName(value),
2580
- describeObjectForErrorMessage(parent, parentPropertyName),
2581
- );
2650
+ callWithDebugContextInDEV(task, () => {
2651
+ console.error(
2652
+ 'Only plain objects can be passed to Client Components from Server Components. ' +
2653
+ '%s objects are not supported.%s',
2654
+ objectName(value),
2655
+ describeObjectForErrorMessage(parent, parentPropertyName),
2656
+ );
2657
+ });
2658
} else if (!isSimpleObject(value)) {
2583
- console.error(
2584
- 'Only plain objects can be passed to Client Components from Server Components. ' +
2585
- 'Classes or other objects with methods are not supported.%s',
2586
- describeObjectForErrorMessage(parent, parentPropertyName),
2587
- );
2588
- } else if (Object.getOwnPropertySymbols) {
2589
- const symbols = Object.getOwnPropertySymbols(value);
2590
- if (symbols.length > 0) {
2659
+ callWithDebugContextInDEV(task, () => {
2660
console.error(
2661
'Only plain objects can be passed to Client Components from Server Components. ' +
2593
- 'Objects with symbol properties like %s are not supported.%s',
2594
- symbols[0].description,
2662
+ 'Classes or other objects with methods are not supported.%s',
2663
describeObjectForErrorMessage(parent, parentPropertyName),
2664
);
2665
+ });
2666
+ } else if (Object.getOwnPropertySymbols) {
2667
+ const symbols = Object.getOwnPropertySymbols(value);
2668
+ if (symbols.length > 0) {
2669
+ callWithDebugContextInDEV(task, () => {
2670
+ console.error(
2671
+ 'Only plain objects can be passed to Client Components from Server Components. ' +
2672
+ 'Objects with symbol properties like %s are not supported.%s',
2673
+ symbols[0].description,
2674
+ describeObjectForErrorMessage(parent, parentPropertyName),
2675
+ );
2676
+ });
2677
}
2678
}
2679
}
2829
);
2830
}
2831
2752
-function logPostpone(request: Request, reason: string): void {
2832
+function logPostpone(
2833
+ request: Request,
2834
+ reason: string,
2835
+ task: Task | null, // DEV-only
2836
+): void {
2837
const prevRequest = currentRequest;
2838
+ // We clear the request context so that console.logs inside the callback doesn't
2839
+ // get forwarded to the client.
2840
currentRequest = null;
2841
try {
2842
const onPostpone = request.onPostpone;
2757
- if (supportsRequestStorage) {
2843
+ if (__DEV__ && task !== null) {
2844
+ if (supportsRequestStorage) {
2845
+ requestStorage.run(
2846
+ undefined,
2847
+ callWithDebugContextInDEV,
2848
+ task,
2849
+ onPostpone,
2850
+ reason,
2851
+ );
2852
+ } else {
2853
+ callWithDebugContextInDEV(task, onPostpone, reason);
2854
+ }
2855
+ } else if (supportsRequestStorage) {
2856
// Exit the request context while running callbacks.
2857
requestStorage.run(undefined, onPostpone, reason);
2858
} else {
2863
}
2864
}
2865
2768
-function logRecoverableError(request: Request, error: mixed): string {
2866
+function logRecoverableError(
2867
+ request: Request,
2868
+ error: mixed,
2869
+ task: Task | null, // DEV-only
2870
+): string {
2871
const prevRequest = currentRequest;
2872
+ // We clear the request context so that console.logs inside the callback doesn't
2873
+ // get forwarded to the client.
2874
currentRequest = null;
2875
let errorDigest;
2876
try {
2877
const onError = request.onError;
2774
- if (supportsRequestStorage) {
2878
+ if (__DEV__ && task !== null) {
2879
+ if (supportsRequestStorage) {
2880
+ errorDigest = requestStorage.run(
2881
+ undefined,
2882
+ callWithDebugContextInDEV,
2883
+ task,
2884
+ onError,
2885
+ error,
2886
+ );
2887
+ } else {
2888
+ errorDigest = callWithDebugContextInDEV(task, onError, error);
2889
+ }
2890
+ } else if (supportsRequestStorage) {
2891
// Exit the request context while running callbacks.
2892
errorDigest = requestStorage.run(undefined, onError, error);
2893
} else {
3683
request.abortableTasks.delete(task);
3684
task.status = ERRORED;
3685
const postponeInstance: Postpone = (x: any);
3570
- logPostpone(request, postponeInstance.message);
3686
+ logPostpone(request, postponeInstance.message, task);
3687
emitPostponeChunk(request, task.id, postponeInstance);
3688
return;
3689
}
3700
3701
request.abortableTasks.delete(task);
3702
task.status = ERRORED;
3587
- const digest = logRecoverableError(request, x);
3703
+ const digest = logRecoverableError(request, x, task);
3704
emitErrorChunk(request, task.id, digest, x);
3705
} finally {
3706
if (__DEV__) {
3745
flushCompletedChunks(request, request.destination);
3746
}
3747
} catch (error) {
3632
- logRecoverableError(request, error);
3748
+ logRecoverableError(request, error, null);
3749
fatalError(request, error);
3750
} finally {
3751
ReactSharedInternals.H = prevDispatcher;
3896
try {
3897
flushCompletedChunks(request, destination);
3898
} catch (error) {
3783
- logRecoverableError(request, error);
3899
+ logRecoverableError(request, error, null);
3900
fatalError(request, error);
3901
}
3902
}
3923
(reason: any).$$typeof === REACT_POSTPONE_TYPE
3924
) {
3925
const postponeInstance: Postpone = (reason: any);
3810
- logPostpone(request, postponeInstance.message);
3926
+ logPostpone(request, postponeInstance.message, null);
3927
emitPostponeChunk(request, errorId, postponeInstance);
3928
} else {
3929
const error =
3936
typeof reason.then === 'function'
3937
? new Error('The render was aborted by the server with a promise.')
3938
: reason;
3823
- const digest = logRecoverableError(request, error);
3939
+ const digest = logRecoverableError(request, error, null);
3940
emitErrorChunk(request, errorId, digest, error);
3941
}
3942
abortableTasks.forEach(task => abortTask(task, request, errorId));
3974
flushCompletedChunks(request, request.destination);
3975
}
3976
} catch (error) {
3861
- logRecoverableError(request, error);
3977
+ logRecoverableError(request, error, null);
3978
fatalError(request, error);
3979
}
3980
}