Bug Report
Describe the Bug
In packages/query-core/src/infiniteQueryBehavior.ts, the internal fetchPage helper function calls Promise.reject() without any argument (i.e., rejects with undefined) when the query has been cancelled via the AbortSignal:
const fetchPage = async (
data: InfiniteData<unknown>,
param: unknown,
previous?: boolean,
): Promise<InfiniteData<unknown>> => {
if (cancelled) {
return Promise.reject() // rejects with undefined, not a proper error
}
// ...
}
The cancelled flag is set to true by the AbortSignal abort event listener (via addConsumeAwareSignal). When abortController.abort() is called in query.ts (inside onCancel), the signal fires and cancelled becomes true. On the next fetchPage invocation within the multi-page fetch loop, the rejection propagates undefined as the error reason rather than a meaningful AbortError (i.e., context.signal.reason).
Impact
- Custom
retry functions receive undefined as the error argument instead of an AbortError, breaking any logic that inspects the error type.
onError / onSettled callbacks in QueryCache config receive undefined as the error.
- Error boundaries using
throwOnError may receive undefined instead of a real error, leading to confusing error messages.
Steps to Reproduce
const client = new QueryClient({
queryCache: new QueryCache({
onError: (error) => {
console.log("error is:", error) // logs: "error is: undefined"
console.log(error instanceof DOMException) // false (should be true)
}
})
})
client.fetchInfiniteQuery({
queryKey: ["items"],
queryFn: async ({ pageParam }) => {
await new Promise(r => setTimeout(r, 200))
return { items: [pageParam], nextCursor: pageParam + 1 }
},
getNextPageParam: (lastPage) => lastPage.nextCursor,
initialPageParam: 0,
pages: 3,
})
// Cancel while fetching page 2 (after ~250ms, first page done)
setTimeout(() => client.cancelQueries({ queryKey: ["items"] }), 250)
Expected Behavior
Promise.reject() should propagate the actual abort reason:
if (cancelled) {
return Promise.reject(context.signal.reason)
// context.signal.reason is a DOMException{ name: "AbortError" }
// when abortController.abort() is called without an explicit reason
}
This matches the Web standard: AbortSignal.reason is automatically set to a DOMException with name "AbortError" when AbortController.abort() is called without an argument.
Environment
@tanstack/query-core: v5.x (latest)
- Affects:
useInfiniteQuery, useSuspenseInfiniteQuery, prefetchInfiniteQuery
Bug Report
Describe the Bug
In
packages/query-core/src/infiniteQueryBehavior.ts, the internalfetchPagehelper function callsPromise.reject()without any argument (i.e., rejects withundefined) when the query has been cancelled via the AbortSignal:The
cancelledflag is set totrueby theAbortSignalabort event listener (viaaddConsumeAwareSignal). WhenabortController.abort()is called inquery.ts(insideonCancel), the signal fires andcancelledbecomestrue. On the nextfetchPageinvocation within the multi-page fetch loop, the rejection propagatesundefinedas the error reason rather than a meaningfulAbortError(i.e.,context.signal.reason).Impact
retryfunctions receiveundefinedas the error argument instead of anAbortError, breaking any logic that inspects the error type.onError/onSettledcallbacks inQueryCacheconfig receiveundefinedas the error.throwOnErrormay receiveundefinedinstead of a real error, leading to confusing error messages.Steps to Reproduce
Expected Behavior
Promise.reject()should propagate the actual abort reason:This matches the Web standard:
AbortSignal.reasonis automatically set to aDOMExceptionwith name"AbortError"whenAbortController.abort()is called without an argument.Environment
@tanstack/query-core: v5.x (latest)useInfiniteQuery,useSuspenseInfiniteQuery,prefetchInfiniteQuery