Skip to content

fix(query-core): use a hydrated query's current 'retry' options when it rejects - #12511

Draft
KATT wants to merge 10 commits into
TanStack:mainfrom
KATT:cursor/hydrated-rejection-retry-271e
Draft

KATT wants to merge 10 commits into
TanStack:mainfrom
KATT:cursor/hydrated-rejection-retry-271e

Conversation

@KATT

@KATT KATT commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Note

We made this PR with AI help and haven't yet done the two steps in TanStack's AI Contribution Policy. We still need to check that we understand all the code, and to rewrite this description in our own words.

🎯 Changes

When dehydrate includes a pending query, hydrate resumes its promise on the client. That fetch starts before any observer mounts, so its retryer took retry and retryDelay from the options the query had at hydration. If useQuery mounted with retry: false, or a different retryDelay, before the server promise rejected, the hydration options still decided whether and when the query retried.

Now, when a query hydrated while pending rejects, its retries take retry and retryDelay from, in order:

  1. the options a useQuery or other observer set itself,
  2. the defaultOptions.queries passed to hydrate or HydrationBoundary,
  3. the client's defaults (defaultOptions.queries and setQueryDefaults).

A useQuery merges client defaults into its options before it creates the observer, so the query can't tell from the observer's options alone which values the caller set. queryClient.defaultQueryOptions therefore records the options it was called with, in a module-level WeakMap keyed by the object it returns. Options objects gain no new properties. The query reads the map only for fetches that resume a hydrated promise. Every other fetch keeps the options it started with, as before.

New tests in hydration.test.tsx:

  • should use the observer's retryDelay when a hydrated query rejects
  • should use the observer's retry when a hydrated query rejects
  • should use the hydrate retry over the client's default retry when a hydrated query rejects

The first two fail without the fix. The third checks that the fix keeps the hydration options over the client's defaults.

This PR doesn't depend on #12512, and they share no code.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested code changes locally with pnpm run test:pr, or these tests do not apply to this pull request. (Ran oxlint, knip, test:types across all TS versions and vitest for query-core and react-query, plus the docs link check.)
  • I have followed the AI contribution policy and fully understand the code in this pull request, including any code generated with AI assistance.

🚀 Release Impact

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

cursoragent and others added 7 commits November 2, 2026 19:53
… 'retry' options

A query hydrated while pending resumes the dehydrated promise through a
retryer created at hydration time, before any observer has set the
query's options. Its retries used the 'retry' and 'retryDelay' the query
had then, so an observer's 'retry: false' or 'retryDelay' was ignored.

Read 'retry' and 'retryDelay' from the query's options at the time of
each failure for fetches that resume an 'initialPromise'. Other fetches
keep the options they started with.

Co-authored-by: alexander <alexander@n1s.se>
… with 'using'

Co-authored-by: alexander <alexander@n1s.se>
… with 'using'

Adds a 'subscribe' helper to @tanstack/query-test-utils that returns a Disposable, and a test for it and for 'clearOnDispose'.

Co-authored-by: alexander <alexander@n1s.se>
Co-authored-by: alexander <alexander@n1s.se>
…ions

Co-authored-by: alexander <alexander@n1s.se>
…de hydration

Client defaults and setQueryDefaults no longer count as set, so they
don't override hydrate.queries.retry.

Co-authored-by: alexander <alexander@n1s.se>
…the observer's retry

Co-authored-by: alexander <alexander@n1s.se>
@changeset-bot

changeset-bot Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c351a29

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

This PR includes changesets to release 24 packages
Name Type
@tanstack/query-core Patch
@tanstack/angular-query-experimental Patch
@tanstack/lit-query Patch
@tanstack/preact-query Patch
@tanstack/query-async-storage-persister Patch
@tanstack/query-broadcast-client-experimental Patch
@tanstack/query-devtools 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/preact-query-devtools Patch
@tanstack/preact-query-persist-client Patch
@tanstack/react-query-devtools Patch
@tanstack/solid-query-devtools Patch
@tanstack/svelte-query-devtools Patch
@tanstack/vue-query-devtools Patch
@tanstack/react-query-persist-client Patch
@tanstack/solid-query-persist-client Patch
@tanstack/svelte-query-persist-client Patch
@tanstack/react-query-next-experimental Patch
@tanstack/eslint-plugin-query 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

coderabbitai Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

cursoragent and others added 3 commits November 3, 2026 15:20
…d them

Use the files' own fake timers and clean up with unsubscribe() and clear(), as
the other tests do. Remove the clearOnDispose, fakeTimers and subscribe helpers
from @tanstack/query-test-utils.

Co-authored-by: alexander <alexander@n1s.se>
A hydrated fetch now reads retry and retryDelay from the query's current
options at each failure, falling back to the options it started with. This
drops the changes to infiniteQueryBehavior.ts, queryClient.ts and utils.ts,
and replaces the hydration tests with two that fail without the fix.

Co-authored-by: alexander <alexander@n1s.se>
A hydrated query's retries take retry and retryDelay from the options an
observer set itself, then the hydration options, then the client's defaults.
defaultQueryOptions records the options it was called with, so the query can
tell an observer's own options from defaults.

Co-authored-by: alexander <alexander@n1s.se>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants