Skip to content

fix(query-core): pass options to fetch in fetchOptimistic so fetchless queries still fetch - #12132

Closed
A-Raphie wants to merge 1 commit into
TanStack:mainfrom
A-Raphie:fix-fetch-optimistic-queryfn
Closed

A-Raphie wants to merge 1 commit into
TanStack:mainfrom
A-Raphie:fix-fetch-optimistic-queryfn

Conversation

@A-Raphie

@A-Raphie A-Raphie commented Oct 19, 2026 •

Copy link
Copy Markdown

fetchOptimistic builds the query from the observer's defaulted options but then calls query.fetch() without them. Since QueryCache.build returns an existing query without applying options, a query that already exists without a queryFn throws Missing queryFn here: the fallback at the top of fetch() only looks at already-attached observers, and in this path none are attached yet - a suspending observer is still bound to the previous query object and only re-binds in a later effect.

This is reachable through broadcastQueryClient (reported in #12129): a query removed in one tab is rebuilt in the other from { queryKey, queryHash } only; if the other tab's fetch never succeeds, no data-bearing update follows, and a mounted useSuspenseQuery for that key crashes with Missing queryFn instead of fetching with its own options. Hydrated and setQueryData-created queries hit the same window.

The fix passes the defaulted options to fetch(), whose setOptions supplies the queryFn. In the normal path this is a no-op (the freshly built query was constructed with exactly these options).

Regression test reproduces the reported sequence (observer bound -> query removed -> rebuilt from key+hash only -> fetchOptimistic): fails with Missing queryFn before, succeeds after. Full queryObserver suite (76/76) and query-broadcast-client-experimental suite (13/13) pass.

Fixes #12129

Summary by CodeRabbit

  • Bug Fixes
    • Optimistic queries can now complete successfully when a cached query is rebuilt without its own query function, using the current observer’s fetch options instead. This prevents fetches in that situation from failing because the rebuilt query lacks a query function.

…s queries still fetch

fetchOptimistic builds the query from the observer's options but then calls query.fetch() without them. QueryCache.build returns an existing query without applying options, so a query that was rebuilt without a queryFn - by broadcastQueryClient from a cross-tab message, or hydrated - throws Missing queryFn when a suspending observer fetches it before any observer has attached: the fallback at the top of fetch() only looks at already-attached observers, and there are none yet in this path.

Reported through query-broadcast-client-experimental: when a query is removed and re-added cross-tab, the rebuilt query stays without a queryFn if the other tab's fetch never succeeds, and a mounted useSuspenseQuery crashes with Missing queryFn on its next optimistic fetch instead of fetching with its own options.

The regression test reproduces that sequence: observer bound -> query removed -> rebuilt from key+hash only -> fetchOptimistic. It fails with Missing queryFn before this change and succeeds after. Full queryObserver suite (76) and broadcast client suite (13) pass.

Fixes TanStack#12129
@changeset-bot

changeset-bot Bot commented Oct 19, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: cb76263

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: TanStack/query/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b0a73439-9557-4bb9-a076-bf49814f3628
📥 Commits

Reviewing files that changed from the base of the PR and between 8c3f218 and cb76263.

📒 Files selected for processing: 2
  • packages/query-core/src/__tests__/queryObserver.test.tsx
  • packages/query-core/src/queryObserver.ts
📝 Walkthrough

QueryObserver.fetchOptimistic now passes its defaulted options to query.fetch. A regression test covers a rebuilt query without a query function when the observer remains bound to the removed query.

Changes: Optimistic query fetch

Layer / File(s) Summary
Fetch with observer options
packages/query-core/src/queryObserver.ts, packages/query-core/src/__tests__/queryObserver.test.tsx
fetchOptimistic passes defaulted observer options to query.fetch. The test checks that the supplied queryFn produces a successful result for a rebuilt query.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: amariahak

Merge Risk: ⚪ Minimal · up to cb762

The change addresses fetching a rebuilt query without a query function, and no outstanding issue was identified that would prevent merging.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Description check Warning The description clearly explains the bug, affected scenarios, implementation, regression test, test results, and linked issue. However, it omits the required Checklist and Release Impact sections, inc… Add the required Checklist and Release Impact sections. Mark each checklist item accurately, state whether a changeset was generated, and identify this as a published-code change or docs/CI/dev-only change.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title is concise, specific, and accurately describes the main change: passing options to fetchOptimistic so queries without a queryFn can still fetch.
Linked Issues check Passed Issue [#12129] requires an already mounted suspending observer to fetch a rebuilt query with the observer's own queryFn. QueryObserver.fetchOptimistic now calls query.fetch(defaultedOptions). Th…
Out of Scope Changes check Passed The pull request changes only QueryObserver.fetchOptimistic and adds its focused regression test. Both changes directly support issue [#12129]. No unrelated behavior or files are changed.
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
Full details: Linked Issues check

Explanation

Issue [#12129] requires an already mounted suspending observer to fetch a rebuilt query with the observer's own queryFn. QueryObserver.fetchOptimistic now calls query.fetch(defaultedOptions). The added regression test removes the original query, rebuilds it without a queryFn, and verifies that the fetch returns data from the supplied function. This implements the linked issue's requested fix.

Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2 files.

Full details: Description check

Explanation

The description clearly explains the bug, affected scenarios, implementation, regression test, test results, and linked issue. However, it omits the required Checklist and Release Impact sections, including the changeset declaration for a published-code change.

Resolution

Add the required Checklist and Release Impact sections. Mark each checklist item accurately, state whether a changeset was generated, and identify this as a published-code change or docs/CI/dev-only change.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • 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

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

@TkDodo

TkDodo commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator

@TkDodo TkDodo closed this Oct 19, 2026
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.

useSuspenseQuery fails with "Missing queryFn" for a query that broadcastQueryClient re-created from another tab

2 participants