ObservabilityIntermediate~1 hour

Catch errors in production

Your app throws exceptions in production that you never see — a user hits a broken checkout, closes the tab, and never tells you. This recipe sets up automatic error tracking so every crash (client and server) reaches you with a stack trace and enough context to actually fix it.

the approach

Errors in production are guaranteed; the goal isn't zero errors, it's zero *silent* errors. Don't rely on users to report bugs or on you scrolling raw logs. Install a dedicated error tracker that captures every uncaught exception, deduplicates them into issues, attaches the context to reproduce (user, route, release, breadcrumbs), and alerts you only when something is new or regressing. Treat it as day-one infrastructure you wire up once — not something you scramble to add after your first incident.

Step by step

  1. 1

    Pick an error tracker

    Sentry is the default and what most vibe coders should use — it covers frontend, backend, and source maps in one SDK. Alternatives worth knowing: Highlight.io (adds session replay), Rollbar, or Better Stack if you want logs and errors together. Don't try to hand-roll this with console.error and a log file; you'll never see the errors that matter.

  2. 2

    Run the official install wizard

    For Next.js, run `npx @sentry/wizard@latest -i nextjs`. It generates the config files, wraps next.config with withSentryConfig, wires up the client, server, and edge runtimes, and drops in your DSN. Frameworks like SvelteKit, Remix, and plain Node have equivalent wizards. Let the tool scaffold it — the manual setup has too many moving parts to get right by hand.

  3. 3

    Store the DSN and auth token as env vars

    The public DSN goes in an env var (NEXT_PUBLIC_SENTRY_DSN for client, SENTRY_DSN for server). The SENTRY_AUTH_TOKEN — needed for uploading source maps — must be set in your build environment (e.g. Vercel project settings), never committed. Set separate environments so staging noise doesn't drown out real production errors.

  4. 4

    Upload source maps and tag releases

    Without source maps, every production stack trace is unreadable minified garbage like `a.b is not a function` at `chunk-4f2.js:1:9021`. The wizard adds a build-time upload step — confirm it runs in CI/Vercel. Tag each release with your git SHA so an error links straight to the commit that caused it.

  5. 5

    Add user and feature context

    A bare exception is hard to triage. Call Sentry.setUser({ id }) after login, and enrich manual captures with tags (feature: 'checkout') and extra data. Then when checkout breaks you can see it's one specific user on one specific route, not a mystery. Scrub secrets first (see pitfalls).

  6. 6

    Set sample rates and silence known noise

    Set tracesSampleRate to 0.1 or lower in production — 1.0 will burn through your quota and bill fast. Use ignoreErrors / beforeSend to drop noise you can't fix (browser extension errors, ResizeObserver warnings, aborted fetches) so real signal isn't buried.

  7. 7

    Route alerts to where you'll see them

    Connect Sentry to Slack or email, but alert on *new issues*, *regressions*, and *spikes* — not every single event. Getting pinged 400 times an hour trains you to ignore alerts entirely. One message when a new bug appears is what you want.

  8. 8

    Verify it actually catches errors

    Throw a deliberate error in a deployed route (`throw new Error('sentry test')`) and confirm it lands in the dashboard within a minute — with a readable, source-mapped stack trace pointing at your real .ts file. If the trace is minified, source maps aren't uploading. Don't trust setup you haven't seen fire.

Starter snippet

typescript
// instrumentation.ts — Next.js runs this before your app boots
import * as Sentry from "@sentry/nextjs";

export async function register() {
  Sentry.init({
    dsn: process.env.SENTRY_DSN,
    environment: process.env.VERCEL_ENV ?? "development",
    tracesSampleRate: 0.1,   // 10% of requests get perf traces
    sendDefaultPii: true,    // attach user + request context
  });
}

// Next.js hands every uncaught server error to this hook
export const onRequestError = Sentry.captureRequestError;

// Anywhere you catch an error yourself, enrich it before sending:
export async function chargeCard(userId: string, cents: number) {
  try {
    await stripe.paymentIntents.create({ amount: cents /* ... */ });
  } catch (err) {
    Sentry.captureException(err, {
      user: { id: userId },
      tags: { feature: "checkout" },
      extra: { cents },
    });
    throw err; // still let the caller handle/surface it
  }
}

✕ Watch out for

  • Minified stack traces: skipping source-map upload means every production error is unreadable, so you can't tell which line broke.
  • Blowing your quota (and bill) by setting tracesSampleRate to 1.0 or capturing every log line — sample aggressively in production.
  • Leaking PII: dumping raw request bodies or headers can ship passwords, auth tokens, and card numbers to your error tracker. Scrub them in beforeSend.
  • Only wiring up one side — catching server errors but missing client-side crashes and unhandled promise rejections, or vice versa.
  • Alert fatigue: paging on every event instead of new/regressed issues, so the team mutes the channel and real incidents get ignored.

✓ Pro tips

  • AI-agent prompt: tell it 'Add Sentry to this Next.js app using `npx @sentry/wizard`, upload source maps in the Vercel build, and tag each release with the git SHA.' Then check three things yourself — the DSN comes from an env var (not hardcoded), source maps actually upload during the build, and it added the onRequestError hook. Agents love to stop right after Sentry.init and skip the rest.
  • Make the agent prove it works: have it deploy, throw a deliberate test error, and show you the event in the dashboard with a readable un-minified stack trace before it claims the task is done.
  • Add a beforeSend hook that strips secrets (passwords, tokens, full card numbers, auth headers) so sensitive data never leaves your app in an error payload.
  • Keep tracesSampleRate at 0.1 or lower in production — full sampling looks fine on day one and torches your quota by the end of the week.