From c96178bf4541c0b9c1410dbacdaf2bda9f648d45 Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Sun, 15 Feb 2026 02:08:23 -0800 Subject: [PATCH 1/2] fix(query-core): handle combine throwing in QueriesObserver#notify When useSuspenseQueries is used with combine and a query transitions to pending/error state (e.g. after resetQueries), the combine function throws because data is undefined despite types narrowing it as defined. The #notify method calls combine as an optimization to check if the combined result changed. If combine throws during this check, we now catch the error and still notify listeners so the framework can re-suspend or show an error boundary. Fixes #10129 --- .../src/__tests__/queriesObserver.test.tsx | 39 +++++++++++++++++++ packages/query-core/src/queriesObserver.ts | 18 ++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/packages/query-core/src/__tests__/queriesObserver.test.tsx b/packages/query-core/src/__tests__/queriesObserver.test.tsx index 2f7080ec60c..7504504331e 100644 --- a/packages/query-core/src/__tests__/queriesObserver.test.tsx +++ b/packages/query-core/src/__tests__/queriesObserver.test.tsx @@ -546,4 +546,43 @@ describe('queriesObserver', () => { trackPropSpy.mockRestore() }) + + test('should still notify listeners when combine throws after query reset', async () => { + const key1 = queryKey() + const queryFn1 = vi.fn().mockReturnValue({ name: 'test' }) + + const combine = vi.fn( + (results: Array) => { + // This simulates a combine function that assumes data is always defined + // (like useSuspenseQueries types suggest) + return results.map((r) => (r.data as { name: string }).name) + }, + ) + + const observer = new QueriesObserver>( + queryClient, + [{ queryKey: key1, queryFn: queryFn1 }], + { combine }, + ) + + const results: Array> = [] + const unsubscribe = observer.subscribe((result) => { + results.push(result) + }) + + // Wait for queries to resolve + await vi.advanceTimersByTimeAsync(0) + + // Reset the query - this transitions it to pending state + // which should cause combine to throw since data is undefined + queryClient.resetQueries({ queryKey: key1 }) + + // The listener should still have been notified despite combine throwing + const lastResult = results[results.length - 1] + expect(lastResult).toBeDefined() + expect(lastResult![0]!.status).toBe('pending') + expect(lastResult![0]!.data).toBeUndefined() + + unsubscribe() + }) }) diff --git a/packages/query-core/src/queriesObserver.ts b/packages/query-core/src/queriesObserver.ts index 67dd088f9ae..965b425a136 100644 --- a/packages/query-core/src/queriesObserver.ts +++ b/packages/query-core/src/queriesObserver.ts @@ -296,9 +296,23 @@ export class QueriesObserver< if (this.hasListeners()) { const previousResult = this.#combinedResult const newTracked = this.#trackResult(this.#result, this.#observerMatches) - const newResult = this.#combineResult(newTracked, this.#options?.combine) - if (previousResult !== newResult) { + let shouldNotify: boolean + try { + const newResult = this.#combineResult( + newTracked, + this.#options?.combine, + ) + shouldNotify = previousResult !== newResult + } catch { + // If combine throws (e.g. when used with useSuspenseQueries and + // a query transitions to pending/error state after a reset), we + // still need to notify so the framework can re-suspend or show + // an error boundary. + shouldNotify = true + } + + if (shouldNotify) { notifyManager.batch(() => { this.listeners.forEach((listener) => { listener(this.#result) From 2caed00eee036bd199b7a795a8a78483e67efd79 Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Sun, 15 Feb 2026 16:14:48 -0800 Subject: [PATCH 2/2] chore: add changeset for combine error handling fix --- .changeset/brave-tigers-wave.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/brave-tigers-wave.md diff --git a/.changeset/brave-tigers-wave.md b/.changeset/brave-tigers-wave.md new file mode 100644 index 00000000000..aed947097c2 --- /dev/null +++ b/.changeset/brave-tigers-wave.md @@ -0,0 +1,5 @@ +--- +'@tanstack/query-core': patch +--- + +fix: handle combine throwing in QueriesObserver notify