From 4bb68f0b5cebafb0b39818b2ddeb04d15f11f789 Mon Sep 17 00:00:00 2001
From: Henos D
Date: Wed, 21 Oct 2026 16:18:44 +0300
Subject: [PATCH] fix(v10/react): Don't crash ErrorBoundary when `error.cause`
is not an Error
Backport of: #25542
Co-Authored-By: GPT-6
---
packages/react/src/error.ts | 4 ++++
packages/react/test/errorboundary.test.tsx | 22 ++++++++++++++++++++++
2 files changed, 26 insertions(+)
diff --git a/packages/react/src/error.ts b/packages/react/src/error.ts
index dff809975dd2..ede06362f0a6 100644
--- a/packages/react/src/error.ts
+++ b/packages/react/src/error.ts
@@ -24,6 +24,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();