The complete error hierarchy: segment vs page vs root error boundaries, global-error.tsx for root layout crashes, the digest prop for server-side error correlation, how errors propagate through nested RSC trees, Suspense + error boundary interaction, and the four error categories (validation, not-found, auth, unexpected) with the right handling pattern for each.
A-17 — Error Architecture and Recovery Patterns
Who this is for: Engineers who know
error.tsxexists and want the complete picture before a production incident forces them to learn it the hard way.
Next.js has four distinct error handling mechanisms. Most developers know one of them. Using the wrong mechanism for the wrong error category produces two failure modes: swallowed errors (the user sees nothing, you debug nothing) or nuclear 500s for recoverable situations that should have been inline validation messages. This module gives you the complete map.
The Four Error Categories
Before touching any code, classify your errors. The classification drives everything else.
| Category | Example | Correct mechanism |
|---|---|---|
| Validation error | "Email already taken", "Price must be positive" | Return from Server Action, display inline |
| Not-found | Product slug doesn't exist, deleted record | notFound() → not-found.tsx |
| Auth error | "You don't have permission to view this" | redirect('/login') or forbidden() |
| Unexpected error | DB timeout, null dereference, third-party API crash | throw → caught by error.tsx |
The rule: anything a user can cause by providing bad input or lacking permissions should never reach error.tsx. Those are expected outcomes of a working system. Only genuinely unexpected failures — things the code should never encounter under normal operation — belong in error.tsx.
Mixing these up is the root cause of most Next.js error handling bugs:
- Throwing on validation → user sees "Something went wrong" instead of "Email already taken"
- Returning on DB timeout → error is silently swallowed, user sees a form that never submits
- Using
notFound()for auth errors → crawler sees a 404 instead of a 401, affecting SEO - Using
error.tsxfor not-found → you lose the 404 status code semantics entirely
error.tsx — Segment-Level Error Boundaries
error.tsx is a React error boundary implemented as a Next.js file convention. It catches unexpected errors thrown from Server Components, Client Components, and Server Actions within the same route segment and all child segments that don't have their own error.tsx.
Under the hood, it wraps the route segment's content in a class-based React error boundary. You write a function component; Next.js handles the class wrapper.
Two props are passed:
error — the Error object. In production, error.message is a generic string ("An error occurred in the Server Components render") — not the original error message. Next.js deliberately strips error details from the client-side error to prevent information leakage. The error.digest is what connects the client-visible error to your server logs.
reset — a function that triggers a re-render of the error boundary's children. More on this below.
The 'use client' directive is not optional. Error boundaries are fundamentally about rendering fallback UI when a component fails to render. Detecting that failure requires React's componentDidCatch lifecycle, which only runs in the browser. Server Components cannot be error boundaries.
The digest — Your Incident Correlation Token
When Next.js catches an error server-side, it:
- Generates a deterministic hash (the digest) from the error details.
- Logs the full error (stack trace, request context) server-side, tagged with the digest.
- Sends only the digest to the client.
The user sees digest: "1234abcd". Your server logs contain the full stack trace tagged digest=1234abcd. You search your logging platform for that digest and find the exact error, the exact component, the exact line.
This is the intended workflow. Show the digest in your error UI. Train your support team to ask for it. Search for it in your observability platform when debugging.
global-error.tsx — Root Layout Crashes
error.tsx has a blind spot: it cannot catch errors thrown by its sibling layout.tsx.
The segment hierarchy matters here. error.tsx at app/error.tsx wraps the page content but lives inside the root layout. If the root layout throws — database connection failure during session initialisation, a crash in your analytics provider — the root layout never finishes rendering. The error boundary inside the layout never gets mounted. Nothing catches the error.
global-error.tsx handles this case:
global-error.tsx is the only Next.js component that must render <html> and <body> tags. It replaces the entire document — not a segment within the layout, but the full HTML output. Your root layout is not available. Your design system is not available. Your fonts are not loaded. This is a bare-bones document.
Keep global-error.tsx minimal. Its job is to tell the user something is catastrophically wrong and give them a reference number for support. It is not a full page — it's an emergency fallback.
In development, Next.js shows a full-page error overlay and ignores global-error.tsx. Test it by running a production build (next build && next start) and intentionally throwing from your root layout.
Error Propagation Through the RSC Tree
Errors propagate upward through the component tree until they hit an error boundary. The nearest ancestor error.tsx catches the error.
Understanding the exact propagation path prevents surprises:
Sign in to keep reading
The rest of this module is free — sign in with Google to unlock it and track your progress.
Sign in & RegisterDiscussion
0Join the discussion