Skip to content

Error Handling in Next.js

Robust apps fail gracefully. This lesson consolidates App Router error tools — error.tsx, global-error.tsx, notFound(), and Server Action / Route Handler failures — into a practical recovery strategy.

Layered Error Handling in the App Router

error.tsx wraps a route segment in a client-side error boundary. Unexpected exceptions during rendering are caught and replace that segment with a fallback UI that receives error and reset. This isolates failures so a broken widget does not take down the entire shell.

notFound() is intentional: use it when a resource is missing (invalid slug, deleted product). Route Handlers and Server Actions should return clear status codes or structured error objects so UI can show validation messages without crashing.

// app/dashboard/error.tsx
"use client";

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <div>
      <h2>Something went wrong</h2>
      <p>{error.message}</p>
      <button onClick={() => reset()}>Try again</button>
    </div>
  );
}

error.tsx must be a Client Component. reset() re-renders the segment after a transient failure (for example a flaky fetch).

Choosing the Right Failure Signal

throw new Error("...")   // unexpected failure → nearest error.tsx
notFound()               // missing resource → nearest not-found.tsx
redirect("/login")       // auth / flow control, not really an "error"
return { error: "..." }  // expected validation failure in a Server Action
  • Unexpected bugs belong in throw / error.tsx recovery.
  • Known missing content belongs in notFound() / not-found.tsx.
  • Auth failures usually use redirect(), not an error boundary.
  • Form validation should return structured errors, not throw, when the failure is expected.

Error Handling Cheat Sheet

Map each failure type to the right Next.js tool.

Failure Tool
Unexpected render / fetch crash error.tsx + reset()
Root layout crash global-error.tsx
Missing resource notFound() + not-found.tsx
Expected form validation Return state via Server Action / useActionState
API client error HTTP status + JSON body from Route Handler

Logging and error.digest

In production, Next.js may omit raw error messages from the client for safety and instead expose an error.digest id. Log the full error server-side (or with a monitoring tool) and show users a friendly message plus the digest so support can look up the incident.

Server Action and Route Handler Errors

Wrap mutations in try/catch, map known cases to returned { error } objects for the form UI, and only throw for truly unexpected failures. For Route Handlers, prefer Response.json({ error }, { status: 400 }) over opaque 500s when the client sent bad input.

"use server";

export async function updateProfile(prevState: unknown, formData: FormData) {
  const name = String(formData.get("name") || "");
  if (name.length < 2) {
    return { error: "Name must be at least 2 characters." };
  }
  try {
    await saveProfile(name);
    return { error: null };
  } catch {
    return { error: "Could not save. Please try again." };
  }
}

Common Mistakes

  • Using error.tsx for expected cases like "item not found" instead of notFound().
  • Showing raw stack traces to end users in production.
  • Throwing from Server Actions for every validation failure instead of returning state.
  • Adding only a leaf error.tsx and no near-root safety net for unexpected crashes.

Key Takeaways

  • error.tsx catches unexpected segment failures; notFound() handles missing resources.
  • global-error.tsx is required for errors thrown from the root layout itself.
  • Expected validation failures should return structured state, not throw.
  • Route Handlers should use clear HTTP status codes and JSON error bodies.
  • Log full errors server-side; show users calm, actionable recovery UI.

Pro Tip

Treat error.tsx, not-found.tsx, and Server Action return shapes as part of your product design — sketch the recovery copy next to the feature itself so empty/error states don't become an afterthought.