Error Handling: Data Fetching & Fatal Application Errors
Best Practices Guide for Managing Application Failures
Introduction
In modern web applications, handling errors gracefully is a core requirement. In Archibald, error handling happens on two levels:
- Data-fetching errors: Failures of individual requests, handled locally through
useFetch/useSuspenseFetchoptions (error,errorBoundary,throwOnError) and React Error Boundaries. - Fatal errors: Unrecoverable render errors inside a hydrated island, handled by the Hydrator's built-in
ErrorBoundaryand itsfatalErrorfallback component.
Fatal errors: the Hydrator's fatalError fallback
Every hydrated island is wrapped in an ErrorBoundary by the Hydrator HOC (withComponentHydration). If a component inside the island throws during render, the boundary catches the error and renders the island's fatal-error fallback instead of crashing the whole page.
- The fallback is configured via the Hydrator's
fatalErrorprop — a render function receivingFallbackProps(the thrownerrorand aresetErrorBoundarycallback), passed to the boundary asfallbackRender. - If you don't provide one, the
DefaultFatalErrorComponentfrom@archibald/storefrontis used. - Because each island has its own boundary, a fatal error in one island (e.g. the header) does not take down the rest of the page.
const HydratedTeaser = withComponentHydration({
component: Teaser,
fatalError: ({ error, resetErrorBoundary }) => (
<div role="alert">
<p>Something went wrong.</p>
<button onClick={resetErrorBoundary}>Retry</button>
</div>
)
});
Data-fetching errors: useFetch options
For errors that originate from data loading, use the error handling built into the data-fetching hooks — see the error handling deep dive and the per-option pages:
error: How long a failed result is kept in the cache (default500ms) before a re-attempt is allowed. Tune it so users see the error long enough, without hammering the API.errorBoundary: SeterrorBoundary: true(together withsuspense: true) to throw fetch errors to the nearest React Error Boundary instead of rendering a half-broken component.throwOnError: Makes the request layer reject on HTTP error status codes so the failure propagates as a real error (per call or globally viaconfig.throwOnError).- For non-Suspense usage, branch on the hook's returned
isError/errorstate and render an inline error UI.
<ErrorBoundary fallback={<ErrorMessage />}>
<Suspense fallback={<LoadingSpinner />}>
<UserList /> {/* useFetch(..., { suspense: true, errorBoundary: true }) */}
</Suspense>
</ErrorBoundary>
useNetworkError
useNetworkError is intended to detect HTTP error statuses (like 404) from the last network request, but its store integration is currently stubbed out: the last HTTP status is hardcoded to 200, so the hook always returns false. Do not rely on it for 404/error views — handle these cases via useFetch error options or your routing layer instead.
For reference, its intended contract:
errorCodes: HTTP status codes to watch for. Defaults to[404].loose: Defaults totrue. Withloose: truethe hook matches when the status is strictly greater than any configured code (status > code), catching whole error ranges with a single entry; withloose: falsethe status must be contained inerrorCodesexactly.
See Also
For a detailed technical breakdown and additional implementation patterns, refer to the following resources:
Key Takeaways
- Let the Hydrator catch fatal errors: Every island is wrapped in an
ErrorBoundary; customize the fallback via thefatalErrorprop, or rely onDefaultFatalErrorComponent. - Handle fetch errors where the data lives: Use
useFetch'serror/errorBoundary/throwOnErroroptions plus Suspense-aware Error Boundaries for critical data, or the returnedisError/errorstate for inline handling. - Don't use
useNetworkError: It is currently hardcoded to a200status and always returnsfalse. - Combine with Logging: When a fatal error is caught, consider logging the error details to a monitoring service (like Sentry) before rendering the error screen.