From 5c1605257fcaa289941b20cb7b03448e48fe0128 Mon Sep 17 00:00:00 2001 From: Yogesh Kumar Date: Thu, 27 Aug 2026 11:40:49 +0530 Subject: [PATCH] fix(react-query): propagate falsy errors from useMutation to the error boundary useMutation gated the error boundary throw on `result.error` being truthy. A mutationFn that rejects with a falsy value, for example `Promise.reject()` or `Promise.reject('')`, leaves the mutation in the error state but never reaches the boundary, so the component keeps rendering as if nothing failed. Gate on `result.isError` instead, matching what useQuery already does through getHasError and what #11305 fixed for useQueries. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_019MfJ49iHsRNpy4oibuXzrz --- .changeset/quiet-pugs-repeat.md | 5 +++ .../src/__tests__/useMutation.test.tsx | 40 +++++++++++++++++++ packages/react-query/src/useMutation.ts | 2 +- 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 .changeset/quiet-pugs-repeat.md diff --git a/.changeset/quiet-pugs-repeat.md b/.changeset/quiet-pugs-repeat.md new file mode 100644 index 00000000000..afe62028665 --- /dev/null +++ b/.changeset/quiet-pugs-repeat.md @@ -0,0 +1,5 @@ +--- +'@tanstack/react-query': patch +--- + +fix(react-query): throw falsy errors from `useMutation` to the error boundary diff --git a/packages/react-query/src/__tests__/useMutation.test.tsx b/packages/react-query/src/__tests__/useMutation.test.tsx index c23b75bf3a4..338cad67e40 100644 --- a/packages/react-query/src/__tests__/useMutation.test.tsx +++ b/packages/react-query/src/__tests__/useMutation.test.tsx @@ -1363,6 +1363,46 @@ describe('useMutation', () => { consoleMock.mockRestore() }) + it('should be able to throw a falsy error when throwOnError is set to true', async () => { + const consoleMock = vi + .spyOn(console, 'error') + .mockImplementation(() => undefined) + function Page() { + const { mutate } = useMutation({ + mutationFn: () => { + return Promise.reject(undefined) + }, + throwOnError: true, + }) + + return ( +
+ +
+ ) + } + + const { getByText, queryByText } = renderWithClient( + queryClient, + ( +
+ error boundary +
+ )} + > + +
, + ) + + fireEvent.click(getByText('mutate')) + + await vi.advanceTimersByTimeAsync(0) + expect(queryByText('error boundary')).not.toBeNull() + + consoleMock.mockRestore() + }) + it('should be able to throw an error when throwOnError is a function that returns true', async () => { const consoleMock = vi .spyOn(console, 'error') diff --git a/packages/react-query/src/useMutation.ts b/packages/react-query/src/useMutation.ts index 240c6f70d88..3f72b96cf96 100644 --- a/packages/react-query/src/useMutation.ts +++ b/packages/react-query/src/useMutation.ts @@ -63,7 +63,7 @@ export function useMutation< ) if ( - result.error && + result.isError && shouldThrowError(observer.options.throwOnError, [result.error]) ) { throw result.error