Skip to content

Conversation

@HyeongSeoku
Copy link

@HyeongSeoku HyeongSeoku commented Jan 8, 2026

🎯 Changes

Fixes: #9938

  • Aligns experimental_prefetchInRender promise rejection behavior with Suspense defaults.
  • When data already exists, errors no longer reject the thenable and therefore do not trigger the Error Boundary (e.g. subsequent pages in useInfiniteQuery).
  • Adds a regression test for the infinite query + promise path.

🧠 Motivation

useSuspenseInfiniteQuery only throws to the Error Boundary when there is no data to show. However, useInfiniteQuery combined with experimental_prefetchInRender was rejecting the promise even when data existed (e.g. page 2 fetch errors), which made Error Boundary appear unexpectedly. This change makes both paths consistent.

🔧 Implementation Notes

  • Core behavior updated in packages/query-core/src/queryObserver.ts.
  • Test added in packages/react-query/src/__tests__/useQuery.promise.test.tsx to ensure refetch/page errors with existing data do not throw to Error Boundary.

🧪 Testing

pnpm --filter @tanstack/react-query test:lib

✅ Checklist

  • Followed contributing guidelines
  • Tests run locally

📷 Optional Evidence

  • Before: fetch next page error triggers Error Boundary
    before_useInfiniteQuery

  • After: fetch next page error does not trigger Error Boundary; isFetchNextPageError: true is shown
    after_useInfiniteQuery

Summary by CodeRabbit

  • Bug Fixes

    • Refined prefetch promise behavior so rejections occur only when no data is available, and resolutions occur only when result data exists—improving Suspense alignment and error handling.
  • Tests

    • Added test ensuring infinite-query pagination errors set fetch-next flags without propagating to app error boundaries.
  • Chores

    • Added release notes entry documenting the prefetch error-handling change.

✏️ Tip: You can customize this high-level summary in your review settings.

@changeset-bot
Copy link

changeset-bot bot commented Jan 8, 2026

🦋 Changeset detected

Latest commit: cf7059e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 19 packages
Name Type
@tanstack/query-core Patch
@tanstack/angular-query-experimental Patch
@tanstack/query-async-storage-persister Patch
@tanstack/query-broadcast-client-experimental Patch
@tanstack/query-persist-client-core Patch
@tanstack/query-sync-storage-persister Patch
@tanstack/react-query Patch
@tanstack/solid-query Patch
@tanstack/svelte-query Patch
@tanstack/vue-query Patch
@tanstack/angular-query-persist-client Patch
@tanstack/react-query-persist-client Patch
@tanstack/solid-query-persist-client Patch
@tanstack/svelte-query-persist-client Patch
@tanstack/react-query-devtools Patch
@tanstack/react-query-next-experimental Patch
@tanstack/solid-query-devtools Patch
@tanstack/svelte-query-devtools Patch
@tanstack/vue-query-devtools Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

Refines thenable handling in queryObserver.ts for experimental_prefetchInRender by distinguishing errors with no data from other results; updates when thenables resolve/reject and when they are recreated. Adds a test ensuring fetch-next errors in useInfiniteQuery do not trigger the error boundary.

Changes

Cohort / File(s) Summary
Thenable resolution & recreation
packages/query-core/src/queryObserver.ts
Introduces hasResultData and isErrorWithoutData; finalizes thenable resolve only when data exists and rejects only for errors without data; adjusts conditions that recreate thenables for prior fulfilled and rejected states.
Test: infinite query fetch-next error
packages/react-query/src/__tests__/useQuery.promise.test.tsx
Adds a test that verifies useInfiniteQuery fetch-next errors set isFetchNextPageError without invoking the error boundary (retry disabled).
Release notes
.changeset/fix-prefetch-error-boundary.md
Adds a changeset noting behavioral change: prefetch-in-render promise rejection aligns with Suspense (throw only when no data).

Sequence Diagram(s)

(omitted — changes do not introduce a multi-component sequential feature flow that benefits from a diagram)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • TkDodo

Poem

🐰 In burrows of code I nibble the seam,
Promises tidy, no wild error scream,
If data's in pocket, the thenable sings,
If empty and broken, it quietly stings,
Hopping on pages, fetching without extremes 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description includes all required sections: clear 'Changes' explaining the fix with issue reference, 'Motivation' detailing why the change was needed, 'Implementation Notes' showing which files changed, 'Testing' instructions, and a completed checklist.
Linked Issues check ✅ Passed The PR fully addresses issue #9938 by aligning useInfiniteQuery + experimental_prefetchInRender behavior with useSuspenseInfiniteQuery, ensuring subsequent-page errors with existing data do not trigger the Error Boundary.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the issue: queryObserver.ts refinement of thenable handling, a regression test for infinite query promise behavior, and a changeset documenting the fix.
Title check ✅ Passed The title directly summarizes the main change: avoiding promise rejection when data exists in the experimental_prefetchInRender feature, which is the core behavioral fix addressed in the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/react-query/src/__tests__/useQuery.promise.test.tsx (1)

1432-1497: Good regression test for the core issue.

The test correctly validates that fetchNextPage errors with existing data don't propagate to the Error Boundary, directly addressing issue #9938. The structure follows existing patterns in this file.

One minor consideration: other tests that involve errors (e.g., lines 496-498) mock console.error to suppress React's error logging noise. Since this test involves an error being thrown (even though it's caught internally), you might see spurious console output during test runs. Consider adding the mock if you observe test noise:

🔧 Optional: Suppress console.error noise
 it('should not throw to error boundary for refetch errors in infinite queries', async () => {
   const key = queryKey()
   const renderStream = createRenderStream({ snapshotDOM: true })
+  const consoleMock = vi
+    .spyOn(console, 'error')
+    .mockImplementation(() => undefined)

   function Page() {
     // ... component code
   }

   // ... test assertions

   expect(rendered.queryByText('error boundary')).toBeNull()
+  consoleMock.mockRestore()
 })
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f616474 and 1619331.

📒 Files selected for processing (2)
  • packages/query-core/src/queryObserver.ts
  • packages/react-query/src/__tests__/useQuery.promise.test.tsx
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: sukvvon
Repo: TanStack/query PR: 9892
File: packages/solid-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx:331-335
Timestamp: 2025-11-22T09:06:05.219Z
Learning: In TanStack/query test files, when a queryFn contains side effects (e.g., setting flags for test verification), prefer async/await syntax for clarity; when there are no side effects, prefer the .then() pattern for conciseness.
📚 Learning: 2025-11-22T09:06:05.219Z
Learnt from: sukvvon
Repo: TanStack/query PR: 9892
File: packages/solid-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx:331-335
Timestamp: 2025-11-22T09:06:05.219Z
Learning: In TanStack/query test files, when a queryFn contains side effects (e.g., setting flags for test verification), prefer async/await syntax for clarity; when there are no side effects, prefer the .then() pattern for conciseness.

Applied to files:

  • packages/query-core/src/queryObserver.ts
  • packages/react-query/src/__tests__/useQuery.promise.test.tsx
🧬 Code graph analysis (2)
packages/query-core/src/queryObserver.ts (1)
packages/query-core/src/thenable.ts (1)
  • PendingThenable (37-37)
packages/react-query/src/__tests__/useQuery.promise.test.tsx (1)
packages/query-core/src/queryObserver.ts (1)
  • query (700-717)
🔇 Additional comments (2)
packages/query-core/src/queryObserver.ts (2)

595-603: Core fix correctly aligns promise behavior with Suspense defaults.

The introduction of hasResultData and isErrorWithoutData flags elegantly distinguishes between:

  1. Initial errors (no data) → reject thenable → trigger Error Boundary
  2. Subsequent errors with existing data → keep thenable resolved → no Error Boundary

This matches how useSuspenseInfiniteQuery handles subsequent page errors, resolving the behavioral inconsistency reported in issue #9938.


626-636: Thenable recreation logic correctly handles state transitions.

The updated conditions ensure proper behavior:

  • Fulfilled → Error with data: isErrorWithoutData is false, so we don't recreate unless data actually changed. The thenable stays resolved, preventing Error Boundary activation.
  • Rejected → Data available: !isErrorWithoutData triggers recreation when data exists, allowing recovery from error states.

@HyeongSeoku HyeongSeoku changed the title PR: Fix error useInfiniteQuery + boundary behavior for experimental_prefetchInRender fix error useInfiniteQuery + boundary behavior for experimental_prefetchInRender Jan 9, 2026
@HyeongSeoku HyeongSeoku changed the title fix error useInfiniteQuery + boundary behavior for experimental_prefetchInRender fix(query-core): avoid throwing promise errors when data exists Jan 9, 2026
@HyeongSeoku HyeongSeoku closed this Jan 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

useInfiniteQuery + experimental_prefetchInRender uses error boundary for subsequent pages

1 participant