diff --git a/packages/react/src/error.ts b/packages/react/src/error.ts index e290a2baf4c9..9656f1497da9 100644 --- a/packages/react/src/error.ts +++ b/packages/react/src/error.ts @@ -15,6 +15,10 @@ export function setCause(error: Error & { cause?: Error }, cause: Error): void { return; } if (error.cause) { + // We can only link to an `Error` cause, not e.g. a string (`new Error('x', { cause: 'y' })`). + if (!isError(error.cause)) { + return; + } seenErrors.add(error); return recurse(error.cause, cause); } diff --git a/packages/react/test/errorboundary.test.tsx b/packages/react/test/errorboundary.test.tsx index 7e0a8fe42e37..86db01177cc8 100644 --- a/packages/react/test/errorboundary.test.tsx +++ b/packages/react/test/errorboundary.test.tsx @@ -489,6 +489,28 @@ describe('ErrorBoundary', () => { expect(cause.message).toEqual(thirdError.message); }); + it('handles when `error.cause` is not an Error', () => { + function CustomBam(): JSX.Element { + const error = new Error('bam'); + // @ts-expect-error Need to set cause on error + error.cause = 'timeout'; + throw error; + } + + const { container } = render( + You have hit an error

} errorComp={}> +

children

+
, + ); + + const btn = screen.getByTestId('errorBtn'); + fireEvent.click(btn); + + expect(container.innerHTML).toContain('You have hit an error'); + expect(mockCaptureException).toHaveBeenCalledTimes(1); + expect(mockCaptureException.mock.calls[0]?.[0].cause).toBe('timeout'); + }); + it('handles when `error.cause` is recursive', () => { const mockOnError = vi.fn();