Skip to content

Fix unhandled promise rejections during de/rehydration of pending queries - #9752

Merged
Ephem merged 1 commit into
TanStack:mainfrom
Ephem:fredrik/fix-unhandled-rejection
Oct 13, 2025
Merged

Fix unhandled promise rejections during de/rehydration of pending queries#9752
Ephem merged 1 commit into
TanStack:mainfrom
Ephem:fredrik/fix-unhandled-rejection

Conversation

@Ephem

@EphemEphem commented Oct 13, 2025

Copy link
Copy Markdown
Collaborator

🎯 Changes

This PR adds two .catch(noop) to handle previously unhandled promise rejection errors when dehydrating and hydrating promises, which happens when de/rehydrating pending queries.

The errors themselves are still propagated to the query client like before, this is just handling two extraneous promises we are not using the output of.

There are still other active bugs stemming from how we use query.fetch to set up a retryer. I took a stab at adding a new query.createRetryer method and use that instead, but turns out this was a bigger refactor so I wanted to get this quick fix out quickly before revisiting that.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • Bug Fixes

    • Prevents unhandled promise rejections during de/rehydration of pending queries, improving app stability and clearer error handling in development.
  • Tests

    • Simplified hydration tests by removing temporary unhandled rejection listeners, reflecting the new safe handling behavior.
  • Chores

    • Patch release updates to reflect stability improvements and ensure consistent behavior across related query packages.

@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6960735

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

This PR includes changesets to release 19 packages
NameType
@tanstack/react-queryPatch
@tanstack/query-corePatch
@tanstack/react-query-devtoolsPatch
@tanstack/react-query-next-experimentalPatch
@tanstack/react-query-persist-clientPatch
@tanstack/angular-query-experimentalPatch
@tanstack/query-async-storage-persisterPatch
@tanstack/query-broadcast-client-experimentalPatch
@tanstack/query-persist-client-corePatch
@tanstack/query-sync-storage-persisterPatch
@tanstack/solid-queryPatch
@tanstack/svelte-queryPatch
@tanstack/vue-queryPatch
@tanstack/angular-query-persist-clientPatch
@tanstack/solid-query-persist-clientPatch
@tanstack/svelte-query-persist-clientPatch
@tanstack/solid-query-devtoolsPatch
@tanstack/svelte-query-devtoolsPatch
@tanstack/vue-query-devtoolsPatch

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

@nx-cloud

nx-cloudBot commented Oct 13, 2025

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 6960735

CommandStatusDurationResult
nx affected --targets=test:sherif,test:knip,tes...✅ Succeeded3m 54sView ↗
nx run-many --target=build --exclude=examples/*...✅ Succeeded1m 17sView ↗

☁️ Nx Cloud last updated this comment at 2025-10-13 18:08:34 UTC

@coderabbitai

coderabbitaiBot commented Oct 13, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a shared dehydration promise with redaction and a no-op catch to prevent unhandled rejections for pending queries, applies a no-op catch during rehydration fetches, updates imports, removes test listeners for unhandledRejection, and adds a changeset for patch bumps to @tanstack/react-query and @tanstack/query-core.

Changes

Cohort / File(s)Summary
De/rehydration promise handling
packages/query-core/src/hydration.ts
Introduces a shared dehydration promise with error redaction, logging in non-production, and a no-op catch; rehydration now attaches a no-op catch to pending fetch promises; imports noop.
Test cleanup
packages/react-query/src/__tests__/HydrationBoundary.test.tsx
Removes setup/teardown of temporary unhandledRejection listeners; tests no longer suppress unhandled rejections explicitly.
Release metadata
.changeset/tall-banks-drop.md
Adds patch changeset for @tanstack/react-query and @tanstack/query-core with note on avoiding unhandled rejections during de/rehydration.

Sequence Diagram(s)

sequenceDiagram
autonumber
participant App
participant QueryClient
participant Serializer as Serializer/Dehydrate
participant Redactor as Error Redactor
participant Logger
rect rgba(230,245,255,0.5)
note over App,QueryClient: Dehydrate pending queries (changed)
App->>QueryClient: dehydrate()
QueryClient->>Serializer: serialize pending query
Serializer->>Redactor: redact non-permitted errors
Redactor-->>Serializer: redacted error/result
Serializer-->>QueryClient: shared promise (with catch(noop))
QueryClient-->>App: DehydratedState (refs shared promise)
end
Note right of Logger: In non-production, log redacted errors
Loading
sequenceDiagram
autonumber
participant App
participant Hydration as HydrationBoundary
participant QueryClient
participant Fetch as Pending Fetch
rect rgba(230,255,230,0.5)
note over App,Hydration: Rehydrate pending queries (changed)
App->>Hydration: useDehydratedState(DehydratedState)
Hydration->>QueryClient: rehydrate()
QueryClient->>Fetch: resume/trigger pending query fetch
Fetch-->>QueryClient: Promise
QueryClient->>Fetch: attach catch(noop) to avoid unhandled rejection
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • manudeli

Poem

I nibble bytes with whiskered care,
Catching promises mid-air.
No thumps of panic, no alarms—
Redacted errs in gentle arms.
Dehydrate, rehydrate—hop, no flop!
My queue is calm—hip, hop, hop. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Title Check✅ PassedThe title clearly and concisely summarizes the primary change by referencing the fix for unhandled promise rejections during de/rehydration of pending queries, matching the core modifications in the pull request.
Description Check✅ PassedThe pull request description follows the required template with all major sections present and clearly describes the changes, includes a completed checklist, and details the release impact including a generated changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee85d16 and 6960735.

📒 Files selected for processing (3)
  • .changeset/tall-banks-drop.md (1 hunks)
  • packages/query-core/src/hydration.ts (4 hunks)
  • packages/react-query/src/__tests__/HydrationBoundary.test.tsx (0 hunks)
💤 Files with no reviewable changes (1)
  • packages/react-query/src/tests/HydrationBoundary.test.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
packages/query-core/src/hydration.ts (2)
packages/query-core/src/query.ts (1)
  • promise (198-200)
packages/query-core/src/utils.ts (1)
  • noop (82-82)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Preview
  • GitHub Check: Test
🔇 Additional comments (6)
packages/query-core/src/hydration.ts (5)

2-2: LGTM: Import added for unhandled rejection handling.

The noop import is correctly added and will be used to prevent unhandled promise rejections while preserving error propagation to the query client.


82-94: LGTM: Error redaction logic correctly implemented.

The promise chain properly:

  • Uses optional chaining for query.promise (may be undefined)
  • Serializes successful results
  • Redacts errors when required while logging originals in non-production
  • Maintains rejection state by returning Promise.reject(...) in both branches

This preserves error propagation to the query client while enabling controlled error redaction.


96-100: LGTM: Unhandled rejection prevention correctly implemented.

The .catch(noop) pattern is the correct approach:

  • Consumes the rejection locally to prevent unhandled rejection warnings
  • Doesn't interfere with downstream error handling (promise still rejects)
  • Uses optional chaining since promise may be undefined

The explanatory comment clearly documents the intent.


113-113: LGTM: Shared promise correctly referenced.

Using the shared promise variable (with noop catch attached) in the return object is correct and ensures the dehydrated promise won't trigger unhandled rejections.


271-277: LGTM: Hydration side also protected from unhandled rejections.

The .catch(noop) on query.fetch() correctly prevents unhandled rejection warnings during rehydration while preserving error propagation through the query client's error handling.

This completes the fix for both dehydration and rehydration flows.

.changeset/tall-banks-drop.md (1)

1-6: LGTM: Changeset correctly documents the fix.

The changeset properly:

  • Marks both affected packages for patch bumps (appropriate for a bug fix)
  • Provides a clear description of the change
  • Follows standard changeset format

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.

@pkg-pr-new

Copy link
Copy Markdown
More templates

@tanstack/angular-query-experimental

npm i https://pkg.pr.new/@tanstack/angular-query-experimental@9752

@tanstack/eslint-plugin-query

npm i https://pkg.pr.new/@tanstack/eslint-plugin-query@9752

@tanstack/query-async-storage-persister

npm i https://pkg.pr.new/@tanstack/query-async-storage-persister@9752

@tanstack/query-broadcast-client-experimental

npm i https://pkg.pr.new/@tanstack/query-broadcast-client-experimental@9752

@tanstack/query-core

npm i https://pkg.pr.new/@tanstack/query-core@9752

@tanstack/query-devtools

npm i https://pkg.pr.new/@tanstack/query-devtools@9752

@tanstack/query-persist-client-core

npm i https://pkg.pr.new/@tanstack/query-persist-client-core@9752

@tanstack/query-sync-storage-persister

npm i https://pkg.pr.new/@tanstack/query-sync-storage-persister@9752

@tanstack/react-query

npm i https://pkg.pr.new/@tanstack/react-query@9752

@tanstack/react-query-devtools

npm i https://pkg.pr.new/@tanstack/react-query-devtools@9752

@tanstack/react-query-next-experimental

npm i https://pkg.pr.new/@tanstack/react-query-next-experimental@9752

@tanstack/react-query-persist-client

npm i https://pkg.pr.new/@tanstack/react-query-persist-client@9752

@tanstack/solid-query

npm i https://pkg.pr.new/@tanstack/solid-query@9752

@tanstack/solid-query-devtools

npm i https://pkg.pr.new/@tanstack/solid-query-devtools@9752

@tanstack/solid-query-persist-client

npm i https://pkg.pr.new/@tanstack/solid-query-persist-client@9752

@tanstack/svelte-query

npm i https://pkg.pr.new/@tanstack/svelte-query@9752

@tanstack/svelte-query-devtools

npm i https://pkg.pr.new/@tanstack/svelte-query-devtools@9752

@tanstack/svelte-query-persist-client

npm i https://pkg.pr.new/@tanstack/svelte-query-persist-client@9752

@tanstack/vue-query

npm i https://pkg.pr.new/@tanstack/vue-query@9752

@tanstack/vue-query-devtools

npm i https://pkg.pr.new/@tanstack/vue-query-devtools@9752

commit: 6960735

@codecov

codecovBot commented Oct 13, 2025

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 59.78%. Comparing base (ee85d16) to head (6960735).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@ Coverage Diff @@## main #9752 +/- ##
===========================================
+ Coverage 45.53% 59.78% +14.24% 
===========================================
Files 196 125 -71 Lines 8327 5555 -2772 Branches 1894 1516 -378 ===========================================
- Hits 3792 3321 -471 + Misses 4093 1935 -2158 + Partials 442 299 -143 
ComponentsCoverage Δ
@tanstack/angular-query-experimental93.85% <ø> (ø)
@tanstack/eslint-plugin-query∅ <ø> (∅)
@tanstack/query-async-storage-persister43.85% <ø> (ø)
@tanstack/query-broadcast-client-experimental24.39% <ø> (ø)
@tanstack/query-codemods∅ <ø> (∅)
@tanstack/query-core97.48% <100.00%> (+<0.01%)⬆️
@tanstack/query-devtools3.48% <ø> (ø)
@tanstack/query-persist-client-core80.00% <ø> (ø)
@tanstack/query-sync-storage-persister84.61% <ø> (ø)
@tanstack/query-test-utils∅ <ø> (∅)
@tanstack/react-query96.01% <ø> (ø)
@tanstack/react-query-devtools10.00% <ø> (ø)
@tanstack/react-query-next-experimental∅ <ø> (∅)
@tanstack/react-query-persist-client100.00% <ø> (ø)
@tanstack/solid-query78.06% <ø> (ø)
@tanstack/solid-query-devtools∅ <ø> (∅)
@tanstack/solid-query-persist-client100.00% <ø> (ø)
@tanstack/svelte-query∅ <ø> (∅)
@tanstack/svelte-query-devtools∅ <ø> (∅)
@tanstack/svelte-query-persist-client∅ <ø> (∅)
@tanstack/vue-query71.10% <ø> (ø)
@tanstack/vue-query-devtools∅ <ø> (∅)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for freeto 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.

2 participants

@Ephem@TkDodo