Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/bright-rabbits-jump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tanstack/query-core": patch
---

Fix retry callback failureCount to start at 1 for the first failure.
8 changes: 4 additions & 4 deletions docs/framework/react/guides/query-retries.md
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down
1 change: 1 addition & 0 deletions docs/framework/react/reference/useMutation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions docs/framework/react/reference/useQuery.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
1 change: 1 addition & 0 deletions docs/framework/solid/reference/useQuery.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
27 changes: 27 additions & 0 deletions packages/query-core/src/__tests__/query.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
2 changes: 1 addition & 1 deletion packages/query-core/src/retryer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
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
Expand Down