# Proper Error Handling in React Query

# ❌ Wrong → Swallowing Errors

```javascript
function useRepos() {
  return useQuery({
    queryKey: ['repos'],
    queryFn: async () => {
      try {
        const response = await fetch('/api/repos')
        if (!response.ok) throw new Error('Failed')
        return response.json()
      } catch (error) {
        console.log('Error:', error) // ❌ Swallows the error!
        // React Query never knows an error occurred
      }
    }
  })
}

// Result: status stays 'success', no retries, broken error handling
```

# ✅ Correct → Let React Query Handle It

```javascript
function useRepos() {
  return useQuery({
    queryKey: ['repos'],
    queryFn: async () => {
      const response = await fetch('/api/repos')
      if (!response.ok) {
        throw new Error(`Request failed: ${response.status}`) // ✅ Throws to RQ
      }
      return response.json()
    },
    retry: 3, // Works because error propagates
    retryDelay: 1000
  })
}
```

# Why This Breaks Everything

When you catch and don't re-throw:

* **No retries** → React Query doesn't know it failed
    
* **status stays 'success'** → Component thinks everything is fine
    
* **No error boundaries** → Can't use global error handling
    
* **Silent failures** → Users never know something went wrong
    

# The Smart Error Handling Setup

```javascript
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      throwOnError: (error, query) => {
        // Only throw to Error Boundary if no cached data
        return typeof query.state.data === 'undefined'
      }
    }
  },
  queryCache: new QueryCache({
    onError: (error, query) => {
      // Show toast if we have cached data (background refetch failed)
      if (typeof query.state.data !== 'undefined') {
        toast.error(`Background sync failed: ${error.message}`)
      }
    }
  })
})
```

This pattern gives you:

* **Error Boundaries** for initial load failures
    
* **Toast notifications** for background failures
    
* **Automatic retries** for transient issues
    
* **Global error handling** without component coupling
    

The key insight: **Let React Query own the error lifecycle, then hook into it where you need to**.
