Network failures are not edge cases — they happen routinely on mobile connections, during backend deployments, and when third-party services degrade. CWE-755 (Improper Handling of Exceptional Conditions) applies when error states are handled for success paths but not for network failures. ISO 25010 reliability.fault-tolerance requires that faults be recoverable. A component that shows a loading spinner indefinitely on a network failure, or renders nothing at all, strands users in an ambiguous state with no self-service recovery. Users without a retry button must reload the entire page, losing any other in-progress work.
Medium because missing retry options on network errors strand users without a recovery path, directly increasing support load and session abandonment.
Add explicit error states with retry buttons to all data-fetching components. With TanStack Query:
const { data, error, refetch, isError } = useQuery({
queryKey: ['items'],
queryFn: fetchItems,
retry: 3,
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
})
if (isError) return (
<div>
<p>Failed to load. Check your connection.</p>
<button onClick={() => refetch()}>Try again</button>
</div>
)
For manual fetch calls without a library, store a retry counter in state and re-trigger the fetch on button click. Never let a component stay in a loading spinner state indefinitely — always transition to an error state with a retry action after a timeout.
ID: saas-error-handling.user-errors.network-errors-retry
Severity: medium
What to look for: Search for data-fetching code — fetch() calls, SWR/React Query/TanStack Query hooks, Axios calls, tRPC client calls. Look for error handling around network requests: (1) Is there a UI state for network failures (connection refused, fetch failed)? (2) Does the error UI include a retry button or automatic retry logic? (3) For React Query/SWR: are retry options configured? (4) For manual fetch calls: is there a retry mechanism or at least a "Try again" button that re-triggers the request? Also look for offline detection (navigator.onLine) or network status handling.
Pass criteria: Count all data-fetching components and classify each by error handling (retry available, error displayed, no handling). Pass if data-fetching error states include a retry mechanism — either a manual "Try again" button that re-runs the query/fetch, or automatic retry configured in the fetching library (e.g., React Query's default 3 retries on failure). At least 80% of data-fetching components must handle errors visibly. Report the ratio: "X of Y data-fetching components provide retry on error."
Fail criteria: Fail if network errors show an error message with no way for the user to retry without refreshing the page. Fail if loading states and success states are handled but error states are missing entirely (the component just shows nothing or stays in a loading spinner).
Skip (N/A) when: The project makes no client-side data fetching (fully server-rendered with no client-side data loading). Signal: no fetch() calls in client components, no SWR/React Query/TanStack Query in dependencies, all data loaded server-side only.
Detail on fail: "React Query used for all data fetching but error states render null; no retry button found in any data-dependent component". Max 500 chars.
Remediation: Network failures are not edge cases — they happen regularly on mobile connections, during deployments, and when third-party services hiccup. A retry button is the minimum acceptable response.
With React Query (TanStack Query):
const { data, error, refetch, isError } = useQuery({ queryKey: ['items'], queryFn: fetchItems })
if (isError) return (
<div>
<p>Failed to load items. Check your connection.</p>
<button onClick={() => refetch()}>Try again</button>
</div>
)
React Query retries failed requests 3 times by default. For critical requests, you can configure exponential backoff:
useQuery({ queryKey: ['items'], queryFn: fetchItems, retry: 3, retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000) })