Describe the bug
fetchQuery may throw an CancelledError { silent: true, revalidate: undefined } under the following conditions:
- the query is stale (otherwise the fetch won't run).
- the query is invalidated at least one tick after the fetch started.
- the query is in an
active state. For example because a component with useQuery is still mounted.
In the real world, this might happen in combination with react-router, when the user submits a form and then navigates to a new (or the same) page. In my case, we were updating a search param to close a modal after a form submission, which then triggered the loader function containing a fetchQuery call. We're also using a mutation observer1 to trigger invalidations, which is why the invalidation happened only after the navigation began.
I fixed the issue by using ensureQueryData instead of fetchQuery. This is the better choice anyways.
Still, this seems like a footgun since it is difficult to catch while testing. Further, if the intended behavior for fetchQuery is to throw a CancelledError on invalidation, it should always do that and not only when a revalidation is triggered.
Your minimal, reproducible example
https://codesandbox.io/p/sandbox/react-query-cancellederror-on-invalidation-d4g259
Steps to reproduce
See steps above
Expected behavior
Option A: The returned promise should consistently reject, regardless of whether an unrelated observer exists.
Option B: The returned promise should consistently resolve with the data returned from this fetch
Option C: The QueryClient should treat pending promises as observers, revalidate automatically and always resolve with the result from the latest revalidation.
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
Tanstack Query adapter
react-query
TanStack Query version
5.50.1
TypeScript version
No response
Additional context
No response
Describe the bug
fetchQuerymay throw anCancelledError { silent: true, revalidate: undefined }under the following conditions:activestate. For example because a component withuseQueryis still mounted.In the real world, this might happen in combination with
react-router, when the user submits a form and then navigates to a new (or the same) page. In my case, we were updating a search param to close a modal after a form submission, which then triggered the loader function containing afetchQuerycall. We're also using a mutation observer1 to trigger invalidations, which is why the invalidation happened only after the navigation began.I fixed the issue by using
ensureQueryDatainstead offetchQuery. This is the better choice anyways.Still, this seems like a footgun since it is difficult to catch while testing. Further, if the intended behavior for
fetchQueryis to throw aCancelledErroron invalidation, it should always do that and not only when a revalidation is triggered.Your minimal, reproducible example
https://codesandbox.io/p/sandbox/react-query-cancellederror-on-invalidation-d4g259
Steps to reproduce
See steps above
Expected behavior
Option A: The returned promise should consistently reject, regardless of whether an unrelated observer exists.
Option B: The returned promise should consistently resolve with the data returned from this fetch
Option C: The QueryClient should treat pending promises as observers, revalidate automatically and always resolve with the result from the latest revalidation.
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
Tanstack Query adapter
react-query
TanStack Query version
5.50.1
TypeScript version
No response
Additional context
No response
Footnotes
inspired by https://tkdodo.eu/blog/automatic-query-invalidation-after-mutations ↩