Skip to content

Query with cancellation breaks when used with useQuery and fetchQuery simultaneously under StrictMode #9440

@longzheng

Description

@longzheng

Describe the bug

UPDATE: since the issue was initially created I've ruled out the useSuspenseQuery, it seems to be caused by the unmount behaviour of StrictMode and useQuery. #9440 (comment)

A single query with cancellation support that is used as both a useQuery suspense query useSuspenseQuery and a manually invoked query queryClient.fetchQuery, after the StrictMode double render, the fetchQuery promise never return, leading to an unexpected inability to fetch API data.

For example this query which consumes the queryFn signal and passes it to fetch

const query = queryOptions({
  queryKey: ['repoData'],
  queryFn: async ({ signal }) => {
    const response = await fetch(
      'https://api.github.com/repos/TanStack/query',
      { signal }
    );
    return await response.json();
  },
});

When used in a component with both useSuspenseQuery and queryClient.fetchQuery

function Example() {
  const { data: suspenseData } = useSuspenseQuery(query);
  console.log('suspense data', suspenseData);

  const { isPending, error, data, isFetching } = useQuery({
    queryKey: ['test'],
    queryFn: async () => {
      const result = await queryClient.fetchQuery(query);
      console.log('queryFn data', data);

      return result;
    },
  });

the await queryClient.fetchQuery() will never return, and thus the useQuery is stuck pending forever.

Note I've disabled automatic retry to make this problem more obvious, otherwise the retry behaviour would workaround it.

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: false,
      refetchOnWindowFocus: false,
    },
  },
});

This problem appears is easily reproducible with <StrictMode> because of the double rendering that appears to be causing the abort signal. I'm not sure if there is another scenario where the query is also aborted leading to this error.

Your minimal, reproducible example

https://stackblitz.com/edit/tanstack-query-qjpbgwfe?file=src%2Findex.tsx&preset=node

Steps to reproduce

  1. Open the page
  2. See "Loading..." state
  3. fetchQuery does not return promise
  4. Open DevTools, suspense data is logged but queryFn data is not logged

Expected behavior

  1. Open the page
  2. See data rendered
  3. fetchQuery returns promise
  4. Open DevTools, suspense data and queryFn data are both logged

How often does this bug happen?

Every time

Screenshots or Videos

Screen.Recording.2025-07-16.223228.mp4
Image

Platform

  • OS: Windows
  • Chrome 138

Tanstack Query adapter

react-query

TanStack Query version

5.83.0

TypeScript version

5.8.3

Additional context

I noticed on the query cancellation documentation page it says

Cancellation does not work when working with Suspense hooks: useSuspenseQuery, useSuspenseQueries and useSuspenseInfiniteQuery.
However I interpret this to mean that useSuspenseQuery will not respect cancellations, not that it will break entirely.

This issue may be related to #7889 and #8060

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions