Capturing Errors | Sentry for Next.js
Source URL: https://docs.sentry.io/platforms/javascript/guides/nextjs/capturing-errors
Capturing Errors | Sentry for Next.js
Section titled “Capturing Errors | Sentry for Next.js”Sentry’s Next.js SDK automatically captures most unhandled errors. However, Next.js has built-in error handling patterns that intercept errors before they reach Sentry. This guide covers when and why you need manual capture.
The SDK captures these without any code:
- Unhandled exceptions in client-side code
- Unhandled promise rejections
- Server errors that crash (API routes, Server Components)
Next.js intercepts errors in these patterns, hiding them from Sentry:
- Error boundaries (
error.tsx) — catch rendering errors for UI recovery - Caught errors — any
try/catchwhere you handle the error gracefully
If you catch an error and don’t re-throw it, Sentry never sees it.
// Automatic: unhandled errors bubble up to Sentrythrow new Error("This is captured automatically");
// Manual needed: you caught it, Sentry doesn't see ittry { await riskyOperation();} catch (error) { // Error is swallowed - add captureException Sentry.captureException(error); return { error: "Something went wrong" };}Next.js error.tsx files catch rendering errors to show a fallback UI. This is good for UX but means errors never reach Sentry’s global handler.
Add captureException in every error boundary to maintain visibility.
app/error.tsx
"use client";
import { useEffect } from "react";import * as Sentry from "@sentry/nextjs";
export default function Error({ error, reset,}: { error: Error & { digest?: string }; reset: () => void;}) { useEffect(() => { Sentry.captureException(error); }, [error]);
return ( <div> <h2>Something went wrong!</h2> <button onClick={() => reset()}>Try again</button> </div> );}global-error.tsx is a last-resort safety net that only triggers when your root layout itself fails. This is rare—most errors are caught by route-level error.tsx files first.
Include <html> and <body> tags since it replaces the entire document.
app/global-error.tsx
"use client";
import { useEffect } from "react";import * as Sentry from "@sentry/nextjs";
export default function GlobalError({ error, reset,}: { error: Error & { digest?: string }; reset: () => void;}) { useEffect(() => { Sentry.captureException(error); }, [error]);
return ( <html> <body> <h2>Something went wrong!</h2> <button onClick={() => reset()}>Try again</button> </body> </html> );}When you catch errors to return graceful responses (in Server Actions, API routes, or middleware), add captureException before returning.
The pattern is the same everywhere: if you catch and don’t throw, call captureException.
app/actions.ts
"use server";
import * as Sentry from "@sentry/nextjs";
export async function createPost(formData: FormData) { try { const post = await db.posts.create({ data: { title: formData.get("title") as string }, }); return { success: true, id: post.id }; } catch (error) { Sentry.captureException(error); return { success: false, error: "Failed to create post" }; }}Add metadata to help debug errors:
- Tags: Searchable in Sentry’s UI for filtering and grouping (e.g., by feature area or user tier)
- Extra: Displayed in event details for debugging values like IDs (not searchable)
Sentry.captureException(error, { tags: { section: "checkout" }, extra: { orderId, userId: user.id },});See Enriching Events for more options like breadcrumbs, user context, and attachments.
For automatic tracing in Server Actions, see withServerActionInstrumentation.
| Scenario | Captured Automatically? | Action Needed |
|---|---|---|
| Unhandled client error | Yes | None |
| Unhandled server crash | Yes | None |
error.tsx boundary | No | Add captureException |
try/catch with graceful return | No | Add captureException |
try/catch that re-throws | Yes | None |
Error boundary placement:
app/├── global-error.tsx # Root layout errors (rare)├── error.tsx # App-wide fallback└── dashboard/ └── error.tsx # Dashboard-specific handling- Error boundary intercepting — Check that
captureExceptionis called in yourerror.tsxfiles - SDK not initialized — Verify config files exist with correct DSN
beforeSendfiltering — Check if a hook is dropping errors
app/error.tsx
"use client";
import { useEffect } from "react";import * as Sentry from "@sentry/nextjs";
export default function Error({ error }: { error: Error }) { useEffect(() => { // This line is required! Sentry.captureException(error); }, [error]);
return <div>Something went wrong</div>;}If you see the same error twice, you’re likely capturing in multiple places. Check that only one handler captures each error.
// Don't capture in both error.tsx AND a parent component// Pick one location per errorServer errors may show minified traces in production. Enable source maps for readable traces.
The digest property on server errors is a hash you can search for in logs.
// Server errors include a digest for debuggingerror: Error & { digest?: string }
// Log it for cross-referencingconsole.error("Error digest:", error.digest);See Source Maps for setup instructions.