# Error Handling Handle errors gracefully in Next.js applications. Reference: https://nextjs.org/docs/app/getting-started/error-handling ## Error Boundaries ### `error.tsx` Catches errors in a route segment and its children: ```tsx 'use client' export default function Error({ error, reset, }: { error: Error & { digest?: string } reset: () => void }) { return (

Something went wrong!

) } ``` **Important:** `error.tsx` must be a Client Component. ### `global-error.tsx` Catches errors in root layout: ```tsx 'use client' export default function GlobalError({ error, reset, }: { error: Error & { digest?: string } reset: () => void }) { return (

Something went wrong!

) } ``` **Important:** Must include `` and `` tags. ## Server Actions: Navigation API Gotcha **Do NOT wrap navigation APIs in try-catch.** They throw special errors that Next.js handles internally. Reference: https://nextjs.org/docs/app/api-reference/functions/redirect#behavior ```tsx 'use server' import { redirect } from 'next/navigation' import { notFound } from 'next/navigation' // Bad: try-catch catches the navigation "error" async function createPost(formData: FormData) { try { const post = await db.post.create({ ... }) redirect(`/posts/${post.id}`) // This throws! } catch (error) { // redirect() throw is caught here - navigation fails! return { error: 'Failed to create post' } } } // Good: Call navigation APIs outside try-catch async function createPost(formData: FormData) { let post try { post = await db.post.create({ ... }) } catch (error) { return { error: 'Failed to create post' } } redirect(`/posts/${post.id}`) // Outside try-catch } // Good: Re-throw navigation errors async function createPost(formData: FormData) { try { const post = await db.post.create({ ... }) redirect(`/posts/${post.id}`) } catch (error) { if (error instanceof Error && error.message === 'NEXT_REDIRECT') { throw error // Re-throw navigation errors } return { error: 'Failed to create post' } } } ``` Same applies to: - `redirect()` - 307 temporary redirect - `permanentRedirect()` - 308 permanent redirect - `notFound()` - 404 not found - `forbidden()` - 403 forbidden - `unauthorized()` - 401 unauthorized Use `unstable_rethrow()` to re-throw these errors in catch blocks: ```tsx import { unstable_rethrow } from 'next/navigation' async function action() { try { // ... redirect('/success') } catch (error) { unstable_rethrow(error) // Re-throws Next.js internal errors return { error: 'Something went wrong' } } } ``` ## Redirects ```tsx import { redirect, permanentRedirect } from 'next/navigation' // 307 Temporary - use for most cases redirect('/new-path') // 308 Permanent - use for URL migrations (cached by browsers) permanentRedirect('/new-url') ``` ## Auth Errors Trigger auth-related error pages: ```tsx import { forbidden, unauthorized } from 'next/navigation' async function Page() { const session = await getSession() if (!session) { unauthorized() // Renders unauthorized.tsx (401) } if (!session.hasAccess) { forbidden() // Renders forbidden.tsx (403) } return } ``` Create corresponding error pages: ```tsx // app/forbidden.tsx export default function Forbidden() { return
You don't have access to this resource
} // app/unauthorized.tsx export default function Unauthorized() { return
Please log in to continue
} ``` ## Not Found ### `not-found.tsx` Custom 404 page for a route segment: ```tsx export default function NotFound() { return (

Not Found

Could not find the requested resource

) } ``` ### Triggering Not Found ```tsx import { notFound } from 'next/navigation' export default async function Page({ params }: { params: Promise<{ id: string }> }) { const { id } = await params const post = await getPost(id) if (!post) { notFound() // Renders closest not-found.tsx } return
{post.title}
} ``` ## Error Hierarchy Errors bubble up to the nearest error boundary: ``` app/ ├── error.tsx # Catches errors from all children ├── blog/ │ ├── error.tsx # Catches errors in /blog/* │ └── [slug]/ │ ├── error.tsx # Catches errors in /blog/[slug] │ └── page.tsx └── layout.tsx # Errors here go to global-error.tsx ```