Skip to content

Conversation

@HyeongSeoku
Copy link

@HyeongSeoku HyeongSeoku commented Jan 9, 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
    • Fixed prefetch promise rejection handling to align with Suspense behavior, ensuring errors trigger error boundaries only when no data is available, rather than on every error occurrence.
    • Improved error state handling for infinite query pagination to prevent unnecessary error boundary activation during fetch operations.

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

@changeset-bot
Copy link

changeset-bot bot commented Jan 9, 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 9, 2026

📝 Walkthrough

Walkthrough

The PR fixes error boundary behavior in useInfiniteQuery with experimental_prefetchInRender by aligning promise rejection logic to only throw when data is unavailable, matching useSuspenseInfiniteQuery behavior for subsequent pages.

Changes

Cohort / File(s) Summary
Changeset Documentation
.changeset/fix-prefetch-error-boundary.md
New patch release note documenting alignment of experimental_prefetchInRender promise rejection behavior with Suspense to only throw when no data is available.
Core Query Logic
packages/query-core/src/queryObserver.ts
Introduces guards (hasResultData, isErrorWithoutData) to differentiate error-with-data vs. error-without-data scenarios. Adjusts finalizeThenableIfPossible to reject only when error lacks data. Updates thenable lifecycle conditions in prefetch path to prevent unnecessary recreation when errors have existing data.
Test Coverage
packages/react-query/src/__tests__/useQuery.promise.test.tsx
Adds test case validating that error boundary is not triggered for refetch errors on subsequent pages in infinite queries, ensuring isFetchNextPageError is set without error boundary invocation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested labels

package: react-query, package: query-core

Suggested reviewers

  • TkDodo

Poem

🐰 A rabbit hops through query streams so fine,
Where promises reject with perfect line—
No more false bounds when data's safe and sound,
Just errors dance where they're truly found! ✨

🚥 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
Title check ✅ Passed The title clearly and specifically describes the main fix: avoiding throwing promise errors when data exists, which directly addresses the core issue.
Description check ✅ Passed The PR description comprehensively covers changes, motivation, implementation details, testing, and includes completed checklist items; all key sections are present and well-documented.
Linked Issues check ✅ Passed The PR successfully addresses issue #9938 by aligning error boundary behavior between useInfiniteQuery + experimental_prefetchInRender and useSuspenseInfiniteQuery, with core logic fixes and regression tests.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the inconsistent error boundary behavior in infinite queries; no unrelated modifications detected in the changeset, core logic, or tests.

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

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent 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 cf7059e.

📒 Files selected for processing (3)
  • .changeset/fix-prefetch-error-boundary.md
  • packages/query-core/src/queryObserver.ts
  • packages/react-query/src/__tests__/useQuery.promise.test.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 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:

  • .changeset/fix-prefetch-error-boundary.md
  • packages/react-query/src/__tests__/useQuery.promise.test.tsx
  • packages/query-core/src/queryObserver.ts
🧬 Code graph analysis (1)
packages/react-query/src/__tests__/useQuery.promise.test.tsx (1)
packages/query-core/src/queryObserver.ts (1)
  • query (700-717)
🔇 Additional comments (6)
.changeset/fix-prefetch-error-boundary.md (1)

1-5: LGTM! Clear and concise changeset.

The changeset appropriately describes the patch-level fix and clearly communicates the behavioral alignment.

packages/query-core/src/queryObserver.ts (4)

595-596: LGTM! Clear guard conditions.

The hasResultData and isErrorWithoutData guards cleanly distinguish between error-with-data (subsequent page errors) and error-without-data (initial load errors), which is the core of this fix.


597-603: LGTM! Correct promise finalization logic.

The updated logic ensures the thenable only rejects when there's an error without data to show, and resolves with data when available. This correctly prevents Error Boundary triggers for subsequent page errors in infinite queries.


627-629: LGTM! Correct recreation logic for fulfilled state.

Properly handles the transition from fulfilled to error-without-data by recreating the thenable, while also recreating when data changes. This ensures the promise state accurately reflects the query state.


632-634: LGTM! Correct recreation logic for rejected state.

Properly handles the transition from rejected to data-available state by recreating the thenable to resolve with data. Also correctly recreates when the error changes. This ensures error-with-data scenarios don't keep the promise rejected.

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

1432-1497: LGTM! Comprehensive regression test.

This test effectively validates the fix by:

  1. Setting up an infinite query with successful first page and failing subsequent page
  2. Verifying that fetchNextPage errors surface as isFetchNextPageError: true
  3. Asserting the Error Boundary is NOT triggered when data exists

The test directly covers the issue described in #9938 and ensures the behavior now aligns with useSuspenseInfiniteQuery.

Based on learnings, the test correctly uses async/await in the queryFn as it contains side effects (throwing errors based on pageParam).


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.

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