Sentry Error Tracking skill for Claude Code

Readable stack traces, quiet quotas, useful alerts. Guides Claude through a Sentry setup that actually helps on-call: every event tagged with release and environment, source maps uploaded from CI with the auth token kept secret, errors kept at full rate while traces and replays are sampled, and noise filtered at the SDK before it burns quota. It finishes with alert rules for new and regressed issues and a weekly triage routine.

  • namesentry-error-tracking
  • size247 lines · ≈2,450 tokens
  • files1
  • licenseMIT
  • updated

What Claude sees before loading it

This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.

description: Sets up and tunes Sentry for web, Node, and Python apps - SDK init per framework, source map upload with debug IDs, releases and commit tracking, environment tagging, error and trace sampling, PII scrubbing, noise filtering, fingerprinting, alert rules, and an issue triage routine. Use when installing Sentry, stack traces show minified code, the quota is burning on noise, alerts are too loud or silent, or when deciding sample rates for tracing and session replay.

Try asking

  • Set up Sentry in our Next.js app with source maps uploaded from GitHub Actions
  • Sentry stack traces are still minified even though we upload source maps - why?
  • We blew through our Sentry quota in three days; cut the noise without losing real errors

Use it when

  • Installing or upgrading a Sentry SDK in web, Node, or Python code
  • Stack traces show minified or unmatched frames
  • Quota is consumed by extension errors, bots, or one noisy issue
  • Alerts are either ignored because of noise or missing real regressions

Skip it when

  • You need vendor-neutral tracing and metrics pipelines - use opentelemetry instead
  • You are digging through raw server logs for a cause - use log-analysis instead

How to check the result

Throw a deliberate test error per runtime in staging and confirm it arrives with the correct release, environment, readable source frames and no auth headers or cookies.

The common mistake it prevents

The release string used for source map upload differs from the one set in Sentry.init, so maps never match and traces stay minified.

~/.claude/skills/sentry-error-tracking/SKILL.md

--- name: sentry-error-tracking description: Sets up and tunes Sentry for web, Node, and Python apps - SDK init per framework, source map upload with debug IDs, releases and commit… license: MIT ---

Sentry is set up well when every event carries a release, environment, and readable stack trace pointing at your source; the quota is spent on real defects rather than browser-extension noise; no secrets or PII leave the app; and alerts page someone only for new or regressed issues that affect users.

Ground rules

  • The DSN is not a secret (it ships to browsers), but the auth token used for source map upload and release creation is. Keep SENTRY_AUTH_TOKEN in CI secrets only, never in code or .env files committed to git.
  • Set release and environment on every SDK init. Without a release you lose regression detection, suspect commits, and source map matching.
  • Errors: keep sampleRate at 1.0 and filter noise instead. Traces, replays, and profiles: sample.
  • Default sendDefaultPii to false. Add user context deliberately (id, maybe email if the privacy policy allows), not by turning on blanket PII.
  • Use the official wizard or framework SDK; avoid hand-wiring @sentry/browser into a framework that has its own SDK (Next.js, SvelteKit, Remix/React Router, Nuxt, Django).

Workflow

- [ ] 1. Inventory runtimes and current SDK versions
- [ ] 2. Install/upgrade SDK per runtime
- [ ] 3. Configure release, environment, sampling, scrubbing
- [ ] 4. Source maps / debug files uploaded in CI
- [ ] 5. Filter noise and set fingerprints
- [ ] 6. Alert rules and ownership
- [ ] 7. Verify with a deliberate test error per runtime

1. Inventory

grep -rn "@sentry/" package.json */package.json 2>/dev/null
grep -rn "sentry-sdk" requirements*.txt pyproject.toml 2>/dev/null
grep -rn "Sentry.init\|sentry_sdk.init" --include=*.{ts,tsx,js,mjs,py} . | grep -v node_modules

Check all @sentry/* packages share one version; mixed versions cause runtime errors.

2. Install

RuntimeInstall
Next.jsnpx @sentry/wizard@latest -i nextjs (creates instrumentation.ts, instrumentation-client.ts, server/edge configs, wraps next.config)
React/Vite SPAnpm i @sentry/react @sentry/vite-plugin
Node/Expressnpm i @sentry/node; init in a file loaded first (node --import ./instrument.mjs app.mjs)
SvelteKit, Nuxt, Remix/React Router, Astronpx @sentry/wizard@latest -i <framework> or that framework’s @sentry/* package
Python (Django, Flask, FastAPI)pip install "sentry-sdk"; integrations auto-enable when the framework is detected
React Native / Exponpx @sentry/wizard@latest -i reactNative

3. Configure

Browser (React):

import * as Sentry from "@sentry/react";

Sentry.init({
  dsn: import.meta.env.VITE_SENTRY_DSN,
  release: import.meta.env.VITE_RELEASE,          // same string CI uses for upload
  environment: import.meta.env.MODE,
  integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()],
  tracesSampleRate: 0.1,
  tracePropagationTargets: [/^\/api\//, /^https:\/\/api\.example\.com/],
  replaysSessionSampleRate: 0.0,
  replaysOnErrorSampleRate: 1.0,
  sendDefaultPii: false,
  ignoreErrors: ["ResizeObserver loop completed with undelivered notifications"],
  denyUrls: [/extensions\//i, /^chrome-extension:\/\//i, /^moz-extension:\/\//i],
});

Node (load before anything else):

// instrument.mjs
import * as Sentry from "@sentry/node";
Sentry.init({
  dsn: process.env.SENTRY_DSN,
  release: process.env.RELEASE,
  environment: process.env.APP_ENV ?? "development",
  tracesSampler: ({ name, inheritOrSampleWith }) =>
    name.includes("/healthz") ? 0 : inheritOrSampleWith(0.1),
  sendDefaultPii: false,
});

Python:

import os, sentry_sdk

sentry_sdk.init(
    dsn=os.environ.get("SENTRY_DSN"),
    release=os.environ.get("RELEASE"),
    environment=os.environ.get("APP_ENV", "development"),
    traces_sample_rate=0.1,
    send_default_pii=False,
)

Sampling defaults:

SignalStart atRaise when
Errors (sampleRate)1.0Never lower; filter noise instead
Traces0.05-0.2 in prod, 1.0 in stagingLow-traffic services: up to 1.0
Replay sessions0-0.05Investigating a UX issue, temporarily
Replay on error0.5-1.0Default high; it is the most useful replay
Health checks, bots, static assets0 via tracesSampler-

Scrubbing: use beforeSend (and beforeSendTransaction) to drop or redact fields your app knows are sensitive; server-side Data Scrubbing in project settings is a second layer, not the only one. Mask all text and inputs in Replay unless a page is explicitly safe.

beforeSend(event) {
  if (event.request?.headers) delete event.request.headers["authorization"];
  if (event.request?.cookies) event.request.cookies = {};
  return event;
}

Context to add: Sentry.setUser({ id }) after login (clear on logout with Sentry.setUser(null)), Sentry.setTag("tenant", id) for filterable dimensions (low cardinality), Sentry.setContext("order", {...}) for detail.

4. Source maps and releases in CI

Prefer the bundler plugin; it injects debug IDs and uploads in one step:

// vite.config.ts
import { sentryVitePlugin } from "@sentry/vite-plugin";
export default defineConfig({
  build: { sourcemap: true },
  plugins: [
    react(),
    sentryVitePlugin({
      org: process.env.SENTRY_ORG,
      project: process.env.SENTRY_PROJECT,
      authToken: process.env.SENTRY_AUTH_TOKEN,
      release: { name: process.env.RELEASE },
      sourcemaps: { filesToDeleteAfterUpload: ["./dist/**/*.map"] },
    }),
  ],
});

Without a plugin, use sentry-cli:

export SENTRY_AUTH_TOKEN SENTRY_ORG SENTRY_PROJECT   # from CI secrets
VERSION="$(git rev-parse --short HEAD)"
sentry-cli releases new "$VERSION"
sentry-cli releases set-commits "$VERSION" --auto
sentry-cli sourcemaps inject ./dist
sentry-cli sourcemaps upload --release="$VERSION" ./dist
sentry-cli releases finalize "$VERSION"
  • The same VERSION must reach the runtime release option. Derive both from the commit SHA in CI.
  • Do not serve .map files publicly unless you intend to; delete after upload or block them at the CDN.
  • Record deploys per environment (the CLI has a deploys command; check sentry-cli --help for the syntax of the installed version) so “Resolved in next release” and release health work.
  • set-commits --auto needs the GitHub/GitLab integration installed and the repo linked.

5. Noise control and grouping

Fix noise at the source in this order:

  1. denyUrls / allowUrls for third-party scripts and extensions.
  2. ignoreErrors for known-benign messages (ResizeObserver, cancelled fetches on navigation, Non-Error promise rejection captured from libraries you cannot fix).
  3. Inbound filters in project settings (legacy browsers, web crawlers, localhost).
  4. Rate limits / spike protection per key so one bad deploy cannot drain the quota.

Grouping:

  • Wrapped or rethrown errors often group too broadly or too narrowly. Set fingerprints for known cases: scope.setFingerprint(["payment-provider-timeout", provider]).
  • Prefer server-side fingerprint and stack-trace rules (project settings) for patterns, so changes apply without deploys.
  • Throw Error objects, never strings or plain objects; capture with Sentry.captureException(err) and add context instead of formatting it into the message (dynamic messages fragment grouping).

6. Alerts and ownership

AlertRuleRoute to
New issue in productionFirst seen, environment=production, level >= errorTeam channel
RegressionIssue changes from resolved to unresolvedOwner of the code (ownership rules)
SpikeIssue seen > N times in 10 min, or affects > N usersOn-call pager
Release healthCrash-free sessions/users drops below target (e.g. 99.5%)Release owner
Performancep95 of key transaction above thresholdOwning team, not pager
  • Configure ownership rules or CODEOWNERS import so issues auto-assign.
  • Alert on users affected, not raw event count, for client apps.
  • Use Crons monitors for scheduled jobs (missed or failed check-ins), not error alerts.

Triage routine

Weekly, sort unresolved production issues by users affected:

  • Real bug with clear owner -> assign, link ticket, set priority.
  • Expected/benign -> fix the code to not throw, or filter it; do not just ignore in UI.
  • Fixed by a deploy -> “Resolve in next release” so regressions reopen it.
  • Third-party noise -> add to denyUrls/inbound filters, then delete the issue.

Gotchas

  • Next.js: client init belongs in instrumentation-client.ts (current versions); server and edge init are imported from register() in instrumentation.ts, and onRequestError should be exported to capture server component errors.
  • Ad blockers drop browser events; use the Next.js tunnelRoute option or a custom tunnel endpoint when data loss matters.
  • Initializing Node Sentry after importing the app breaks auto-instrumentation; it must load first (--import).
  • Source maps uploaded for release A do not apply to events from release B; mismatched release strings are the usual cause of minified traces. Debug IDs remove most of this pain, so prefer plugin-based upload.
  • Tags with high cardinality (user IDs, URLs with IDs) degrade search; use context or user fields.

Verification

  1. Trigger a test error per runtime (a temporary route throwing new Error("sentry test")) in staging. Confirm it arrives with the right release, environment, readable source frames, and suspect commits.
  2. Confirm no Authorization header, cookies, or tokens appear in the event JSON.
  3. Check the stats page after a day: dropped vs accepted, top noisy issues.
  4. Fire each alert rule once (or use rule preview) and confirm it reaches the right destination. Remove the test route afterwards.

About this skill

Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.

esc