|  | 
|  | 1 | +--- | 
|  | 2 | +id: background-retry-pausing | 
|  | 3 | +title: Background Retry Pausing | 
|  | 4 | +--- | 
|  | 5 | + | 
|  | 6 | +When a query fails and retries are enabled, TanStack Query will pause retry attempts when the browser tab loses focus. This behavior ensures that background tabs don't consume unnecessary resources, but it can create unexpected results when combined with `refetchIntervalInBackground`. | 
|  | 7 | + | 
|  | 8 | +## How Retry Pausing Works | 
|  | 9 | + | 
|  | 10 | +By default, **query retries pause when the browser tab is unfocused** and resume when the tab regains focus. This applies to all retry attempts, including: | 
|  | 11 | + | 
|  | 12 | +- Initial query failures | 
|  | 13 | +- Refetch interval failures | 
|  | 14 | +- Manual refetch failures | 
|  | 15 | + | 
|  | 16 | +[//]: # 'Example' | 
|  | 17 | + | 
|  | 18 | +```tsx | 
|  | 19 | +import { useQuery } from '@tanstack/react-query' | 
|  | 20 | + | 
|  | 21 | +// This query will pause retries when tab loses focus | 
|  | 22 | +const { data, error } = useQuery({ | 
|  | 23 | +  queryKey: ['posts'], | 
|  | 24 | +  queryFn: fetchPosts, | 
|  | 25 | +  retry: 3, // Will pause retries in background | 
|  | 26 | +  retryDelay: 1000, | 
|  | 27 | +}) | 
|  | 28 | +``` | 
|  | 29 | + | 
|  | 30 | +[//]: # 'Example' | 
|  | 31 | + | 
|  | 32 | +When the tab becomes unfocused: | 
|  | 33 | + | 
|  | 34 | +1. Any ongoing retry sequence pauses | 
|  | 35 | +2. The query remains in a `pending` state | 
|  | 36 | +3. Retries resume when the tab regains focus | 
|  | 37 | + | 
|  | 38 | +## The refetchIntervalInBackground Limitation | 
|  | 39 | + | 
|  | 40 | +The `refetchIntervalInBackground` option controls whether queries should continue refetching when the tab is in the background. However, **this option does not affect retry behavior** - retries still pause regardless of this setting. | 
|  | 41 | + | 
|  | 42 | +[//]: # 'Example2' | 
|  | 43 | + | 
|  | 44 | +```tsx | 
|  | 45 | +// Retries still pause when unfocused, even with refetchIntervalInBackground: true | 
|  | 46 | +const { data } = useQuery({ | 
|  | 47 | +  queryKey: ['live-data'], | 
|  | 48 | +  queryFn: fetchLiveData, | 
|  | 49 | +  refetchInterval: 30000, | 
|  | 50 | +  refetchIntervalInBackground: true, // Only affects scheduled refetches | 
|  | 51 | +  retry: 3, // These retries will still pause in background | 
|  | 52 | +}) | 
|  | 53 | +``` | 
|  | 54 | + | 
|  | 55 | +[//]: # 'Example2' | 
|  | 56 | + | 
|  | 57 | +## When This Becomes Problematic | 
|  | 58 | + | 
|  | 59 | +This behavior can cause issues in applications that need reliable background operation | 
|  | 60 | + | 
|  | 61 | +[//]: # 'Example3' | 
|  | 62 | + | 
|  | 63 | +```tsx | 
|  | 64 | +// Real-world example: polling todo list with retries | 
|  | 65 | +const { data } = useQuery({ | 
|  | 66 | +  queryKey: ['todos'], | 
|  | 67 | +  queryFn: fetchTodos, | 
|  | 68 | +  refetchInterval: 60000, // Poll every minute | 
|  | 69 | +  refetchIntervalInBackground: true, // Should work in background | 
|  | 70 | +  retry: 3, // Retry on failure | 
|  | 71 | +}) | 
|  | 72 | + | 
|  | 73 | +// Expected: ~12 requests in 3 minutes with network issues | 
|  | 74 | +// Actual: Only 2 requests when tab is inactive | 
|  | 75 | +``` | 
|  | 76 | + | 
|  | 77 | +[//]: # 'Example3' | 
|  | 78 | + | 
|  | 79 | +In the above example, if the network fails while the tab is inactive, retries pause until the user returns to the tab. This defeats the purpose of `refetchIntervalInBackground` for applications that need continuous background synchronization. | 
|  | 80 | + | 
|  | 81 | +## Alternative Approaches | 
|  | 82 | + | 
|  | 83 | +If you need background retries, you can use custom retry logic: | 
|  | 84 | + | 
|  | 85 | +### Custom Retry Function | 
|  | 86 | + | 
|  | 87 | +[//]: # 'Example4' | 
|  | 88 | + | 
|  | 89 | +```tsx | 
|  | 90 | +import { focusManager } from '@tanstack/react-query' | 
|  | 91 | + | 
|  | 92 | +const { data } = useQuery({ | 
|  | 93 | +  queryKey: ['critical-data'], | 
|  | 94 | +  queryFn: fetchData, | 
|  | 95 | +  refetchInterval: 30000, | 
|  | 96 | +  refetchIntervalInBackground: true, | 
|  | 97 | +  retry: (failureCount, error) => { | 
|  | 98 | +    // Custom logic for background retries | 
|  | 99 | +    if (!focusManager.isFocused()) { | 
|  | 100 | +      // Limit background retries to prevent resource abuse | 
|  | 101 | +      return failureCount < 2 && error.name === 'NetworkError' | 
|  | 102 | +    } | 
|  | 103 | +    // Normal retry behavior when focused | 
|  | 104 | +    return failureCount < 3 | 
|  | 105 | +  }, | 
|  | 106 | +}) | 
|  | 107 | +``` | 
|  | 108 | + | 
|  | 109 | +[//]: # 'Example4' | 
|  | 110 | + | 
|  | 111 | +### Disabling Background Retries | 
|  | 112 | + | 
|  | 113 | +[//]: # 'Example5' | 
|  | 114 | + | 
|  | 115 | +```tsx | 
|  | 116 | +const { data } = useQuery({ | 
|  | 117 | +  queryKey: ['non-critical-data'], | 
|  | 118 | +  queryFn: fetchData, | 
|  | 119 | +  refetchInterval: 30000, | 
|  | 120 | +  refetchIntervalInBackground: true, | 
|  | 121 | +  retry: (failureCount, error) => { | 
|  | 122 | +    // Only retry when tab is focused | 
|  | 123 | +    return focusManager.isFocused() ? failureCount < 3 : false | 
|  | 124 | +  }, | 
|  | 125 | +}) | 
|  | 126 | +``` | 
|  | 127 | + | 
|  | 128 | +[//]: # 'Example5' | 
|  | 129 | +[//]: # 'Info' | 
|  | 130 | + | 
|  | 131 | +> **Note:** These workarounds have limitations and may not fully solve the underlying architectural constraint. The retry pausing behavior is by design to prevent unnecessary resource consumption in background tabs. | 
|  | 132 | +
 | 
|  | 133 | +[//]: # 'Info' | 
0 commit comments