diff --git a/.changeset/bright-rabbits-jump.md b/.changeset/bright-rabbits-jump.md new file mode 100644 index 0000000000..33967681b1 --- /dev/null +++ b/.changeset/bright-rabbits-jump.md @@ -0,0 +1,5 @@ +--- +"@tanstack/query-core": patch +--- + +Fix retry callback failureCount to start at 1 for the first failure. diff --git a/docs/framework/react/guides/query-retries.md b/docs/framework/react/guides/query-retries.md index fa1364dfba..62db2032b4 100644 --- a/docs/framework/react/guides/query-retries.md +++ b/docs/framework/react/guides/query-retries.md @@ -7,10 +7,10 @@ When a `useQuery` query fails (the query function throws an error), TanStack Que You can configure retries both on a global level and an individual query level. -- Setting `retry = false` will disable retries. -- Setting `retry = 6` will retry failing requests 6 times before showing the final error thrown by the function. -- Setting `retry = true` will infinitely retry failing requests. -- Setting `retry = (failureCount, error) => ...` allows for custom logic based on why the request failed. +- `retry = false` disables retries. +- `retry = 6` will retry failing requests 6 times before showing the final error thrown by the function. +- `retry = true` will infinitely retry failing requests. +- `retry = (failureCount, error) => ...` allows for custom logic based on why the request failed. The `failureCount` starts at `1` for the first failure. [//]: # 'Info' diff --git a/docs/framework/react/reference/useMutation.md b/docs/framework/react/reference/useMutation.md index 60940ba5de..39200847a5 100644 --- a/docs/framework/react/reference/useMutation.md +++ b/docs/framework/react/reference/useMutation.md @@ -86,6 +86,7 @@ mutate(variables, { - If `false`, failed mutations will not retry. - If `true`, failed mutations will retry infinitely. - If set to an `number`, e.g. `3`, failed mutations will retry until the failed mutations count meets that number. + - When set to a function, `failureCount` starts at `1` for the first failed attempt. - `retryDelay: number | (retryAttempt: number, error: TError) => number` - This function receives a `retryAttempt` integer and the actual Error and returns the delay to apply before the next attempt in milliseconds. - A function like `attempt => Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)` applies exponential backoff. diff --git a/docs/framework/react/reference/useQuery.md b/docs/framework/react/reference/useQuery.md index d3b10142a8..d5f982243d 100644 --- a/docs/framework/react/reference/useQuery.md +++ b/docs/framework/react/reference/useQuery.md @@ -84,6 +84,7 @@ const { - If `false`, failed queries will not retry by default. - If `true`, failed queries will retry infinitely. - If set to a `number`, e.g. `3`, failed queries will retry until the failed query count meets that number. + - When set to a function, `failureCount` starts at `1` for the first failed attempt. - defaults to `3` on the client and `0` on the server - `retryOnMount: boolean` - If set to `false`, the query will not be retried on mount if it contains an error. Defaults to `true`. diff --git a/docs/framework/solid/reference/useQuery.md b/docs/framework/solid/reference/useQuery.md index 38b9751d75..647e5af69e 100644 --- a/docs/framework/solid/reference/useQuery.md +++ b/docs/framework/solid/reference/useQuery.md @@ -276,6 +276,7 @@ function App() { - If `false`, failed queries will not retry by default. - If `true`, failed queries will retry infinitely. - If set to a `number`, e.g. `3`, failed queries will retry until the failed query count meets that number. + - When set to a function, `failureCount` starts at `1` for the first failed attempt. - defaults to `3` on the client and `0` on the server - ##### `retryOnMount: boolean` - If set to `false`, the query will not be retried on mount if it contains an error. Defaults to `true`. diff --git a/packages/query-core/src/__tests__/query.test.tsx b/packages/query-core/src/__tests__/query.test.tsx index a33bf75cfc..cc9f23d77b 100644 --- a/packages/query-core/src/__tests__/query.test.tsx +++ b/packages/query-core/src/__tests__/query.test.tsx @@ -153,6 +153,33 @@ describe('query', () => { onlineMock.mockRestore() }) + it('should pass a 1-based failureCount to retry callbacks', async () => { + const key = queryKey() + const failureCounts: number[] = [] + let attempt = 0 + + const promise = queryClient.fetchQuery({ + queryKey: key, + queryFn: () => { + attempt++ + if (attempt < 3) { + throw new Error(`error${attempt}`) + } + return `data${attempt}` + }, + retry: (failureCount) => { + failureCounts.push(failureCount) + return failureCount < 3 + }, + retryDelay: 1, + }) + + await vi.advanceTimersByTimeAsync(10) + + await expect(promise).resolves.toBe('data3') + expect(failureCounts).toEqual([1, 2]) + }) + it('should throw a CancelledError when a paused query is cancelled', async () => { const key = queryKey() diff --git a/packages/query-core/src/retryer.ts b/packages/query-core/src/retryer.ts index f4ada851c9..19be7c7d76 100644 --- a/packages/query-core/src/retryer.ts +++ b/packages/query-core/src/retryer.ts @@ -175,7 +175,7 @@ export function createRetryer( const shouldRetry = retry === true || (typeof retry === 'number' && failureCount < retry) || - (typeof retry === 'function' && retry(failureCount, error)) + (typeof retry === 'function' && retry(failureCount + 1, error)) if (isRetryCancelled || !shouldRetry) { // We are done if the query does not need to be retried