[fiber] bugfix - don't show <Offscreen> in error message. (#35763)

## Overview

While building the RSC sandboxes I notice error messages like:

>  An error occurred in the `<Offscreen>` component

This is an internal component so it should show either:

>  An error occurred in the `<Suspense>` component.

>  An error occurred in the `<Activity>` component.

It should only happen when there's a lazy in the direct child position
of a `<Suspense>` or `<Activity>` component.
This commit is contained in:
Ricky
2026-02-11 11:20:51 -05:00
committed by GitHub
parent cd515d7e22
commit 892c68605c
2 changed files with 65 additions and 1 deletions

View File

@@ -213,6 +213,67 @@ describe('ReactIncrementalErrorLogging', () => {
}).toThrow('logCapturedError error');
});
it('does not report internal Offscreen component for errors thrown during reconciliation inside Suspense', async () => {
// When a child of Suspense throws during reconciliation (not render),
// a Throw fiber is created whose .return is the internal Offscreen fiber.
// We should skip Offscreen since it's an internal
// implementation detail and walk up to Suspense instead.
const lazyChild = React.lazy(() => {
throw new Error('lazy init error');
});
await fakeAct(() => {
ReactNoop.render(
<React.Suspense fallback={<div />}>{lazyChild}</React.Suspense>,
);
});
expect(uncaughtExceptionMock).toHaveBeenCalledTimes(1);
expect(uncaughtExceptionMock).toHaveBeenCalledWith(
expect.objectContaining({
message: 'lazy init error',
}),
);
if (__DEV__) {
expect(console.warn).toHaveBeenCalledTimes(1);
expect(console.warn.mock.calls[0]).toEqual([
'%s\n\n%s\n',
'An error occurred in the <Suspense> component.',
'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.',
]);
}
});
it('does not report internal Offscreen component for errors thrown during reconciliation inside Activity', async () => {
// Same as the Suspense test above — Activity also wraps its children in
// an internal Offscreen fiber. The error message should show Activity,
// not Offscreen.
const lazyChild = React.lazy(() => {
throw new Error('lazy init error');
});
await fakeAct(() => {
ReactNoop.render(
<React.Activity mode="visible">{lazyChild}</React.Activity>,
);
});
expect(uncaughtExceptionMock).toHaveBeenCalledTimes(1);
expect(uncaughtExceptionMock).toHaveBeenCalledWith(
expect.objectContaining({
message: 'lazy init error',
}),
);
if (__DEV__) {
expect(console.warn).toHaveBeenCalledTimes(1);
expect(console.warn.mock.calls[0]).toEqual([
'%s\n\n%s\n',
'An error occurred in the <Activity> component.',
'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.',
]);
}
});
it('resets instance variables before unmounting failed node', async () => {
class ErrorBoundary extends React.Component {
state = {error: null};

View File

@@ -122,7 +122,10 @@ export default function getComponentNameFromFiber(fiber: Fiber): string | null {
}
return 'Mode';
case OffscreenComponent:
return 'Offscreen';
if (fiber.return !== null) {
return getComponentNameFromFiber(fiber.return);
}
return null;
case Profiler:
return 'Profiler';
case ScopeComponent: