New to Claude Skills? Learn how to install them →

langfuse on GitHub

Sentry Instrumentation

Free

Optimize error reporting to Sentry for better observability.

by langfuse32.8k stars on langfuse/langfuse
1 views
Updated Aug 10, 2026
Get this skill

Free · Opens the source repo

What Sentry Instrumentation does

Sentry Instrumentation provides a structured approach to error reporting in your web applications. It emphasizes that every event sent to Sentry should represent a promise for human action, ensuring that only actionable errors are reported. By focusing on legibility and routability, it guides developers to send real Error objects with stack traces, rather than vague strings or objects. This precision helps in reducing noise in Sentry, making it easier for teams to triage and respond to genuine issues.

When modifying or adding error paths in your web/** codebase, this skill encourages developers to make deliberate decisions about error capture. It outlines a decision tree to determine whether to capture an error based on its nature—whether it’s user-facing, a transport failure, or a code failure. This clarity helps avoid unnecessary captures that could clutter your error reporting, ensuring that only significant issues are logged.

The skill also introduces best practices for handling errors in a consistent manner. It recommends using shared helper functions for capturing errors, ensuring that all errors are tagged appropriately and logged in a way that prevents duplication in Sentry. For instance, it suggests using the captureUnknownError function for unknown errors and classifying tRPC errors at a single chokepoint to maintain clarity and consistency in error reporting.

Overall, Sentry Instrumentation is designed for developers who want to enhance their error reporting strategy, ensuring that their applications are not only robust but also maintainable. By following its guidelines, teams can improve their observability practices and reduce the noise in their error tracking systems.

When to use it

Use this skill when modifying error handling paths in your web applications to ensure effective error reporting to Sentry.

When not to use it

This skill may not be suitable for applications that do not use Sentry for error tracking or for teams that prefer a more relaxed approach to error logging.

What you can build with it

Modifying Error Paths

When updating error handling in your web application, use this skill to guide decisions on whether to capture errors.

Reducing Sentry Noise

Implement the guidelines to filter out non-actionable errors, improving the signal-to-noise ratio in your Sentry dashboard.

Enhancing Team Collaboration

Use the structured approach to error reporting to facilitate better communication and understanding among team members regarding error handling.

How to install Sentry Instrumentation

View source

1. Install with the skills CLI

npx skills add langfuse/langfuse/sentry-instrumentation --agent claude-code

2. Or install it manually

Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.

Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs

Inside SKILL.md

Written by langfuse

Sentry Instrumentation

One idea: an event in Sentry is a promise that a human should act. If nobody would act on it, do not send it. If you send it, make it legible (a real Error with a stack — never a string/object/SyntheticEvent) and routable (an area tag, a message stable enough to group). Sentry is not a log sink; the server-side observability stack is where firehose logs live.

Decide capture when adding an error path

When adding or changing an error path in web/**, decide explicitly whether it should reach Sentry — do not captureException (or console.error) reflexively. Run the decision tree, then say in one line what you chose and why (in the plan or PR description). The three outcomes:

The failure is…DoNever
an expected user-facing state — a missing/forbidden resource, expired session, invalid user input, a malformed URL in user contentrender the UX (error page / toast / plain text)capture — it is the product working as designed
a transport / offline / infra failure — fetch failed, a 5xx on a poll, an LB bliplet the UI degrade; the server owns this signalcapture client-side — it is an amplified, lower-fidelity copy of a server truth
our code failed — an invariant broke, a parse threw, a worker failed to loadcaptureException a real Error with an area tag via the shared helperspass a raw string/object/Event

console.error is a capture API here. instrumentation-client.ts enables captureConsoleIntegration({ levels: ["error"] }), so every console.error becomes a Sentry event. Logging an object yields an opaque [object Object] fingerprint; logging non-actionable info still mints an issue. For non-actionable logging use console.warn; to report a real failure, capture it properly (below).

The Langfuse pattern (match it exactly)

  • Report caught unknowns through the shared helpers, not raw captureException:
    • captureUnknownError(context, value, extra?) — turns any caught value into a legible Error (real Errors pass through with their stack; everything else is synthesized), tags area, and logs via console.warn so the console integration does not double-capture.
    • reportParserWorkerError(hook, event) — extracts the real fields from a Worker ErrorEvent (message/filename/lineno) instead of stringifying it to [object ErrorEvent].
  • Filter predicates belong in web/src/utils/sentryFilters.ts (documented, unit-tested), called from beforeSend in web/instrumentation-client.ts (the only Sentry.init in the codebase — beforeSend, captureConsoleIntegration, denyUrls, replay masking). Add every new filter as a named predicate there, never as an ad-hoc inline check. beforeSend now holds NO inline checks — every filter routes through a named predicate (isNoisyHttpClientPollEvent, isDenylistedNoiseEvent, isReactDevtoolsInternalEvent). Keep it that way.
  • Classify tRPC errors at the seam, not per call site: handleTrpcError (web/src/utils/api.ts) is the single chokepoint every query/mutation error flows through — the place to drop expected codes and tag the rest (the seam-classification lever, PR #15243).
  • Tag area, keep the message static. captureException(err, { tags: { area }, extra }). Fingerprints group on the message, so put variable IDs in extra, never in the message string.

The rules (each earned the hard way — cited to workstream PRs)

PR references name the lever that established each pattern. Verify a cited symbol or swap against the current tree before relying on it — the code, not this doc, is the source of truth.

  1. An event is a promise a human acts — expected states are not events. A NOT_FOUND / FORBIDDEN / UNAUTHORIZED the UI already renders is not a regression. ⚠ TRPCClientError: Trace not found was the #2 issue by volume (~30k events); PR #15243 drops those at the tRPC seam and switches the "Project Not Found" state (which captured on every mount via ErrorPageWithSentry) to the non-capturing ErrorPage.

  2. Capture a REAL Error, never a string / object / SyntheticEvent / ErrorEvent. Those collapse to opaque [object Object] / [object ErrorEvent] fingerprints with no stack. ⚠ The 5EX cluster (opaque non-Error captures, PR #15175) and the parse-worker [object ErrorEvent] family (PR #15173) were exactly this. Route unknowns through captureUnknownError / reportParserWorkerError.

  3. console.error mints a Sentry event — pick the level deliberately. Use console.warn for non-actionable logs (the #15173 pattern); use captureException for real failures. Never console.error(someObject).

  4. Client transport failure ≠ app bug — do not amplify server/infra state. One LB 502 or a poll blip becomes thousands of browser events. ⚠ The 418 HTTP Client Error saga (~44k events → 8/24h after scoping the httpClient integration, PR #15145) and the next-auth CLIENT_FETCH_ERROR / Failed to fetch families (denylist, PR #15238). The real outage is visible server-side; the client copy is noise.

  5. Root-cause at the source beats a filter. Prefer eliminating the emission over suppressing the event. ⚠ User-content markdown URLs rendered through a Next.js <Link> logged Invalid href … passed to next/router (~40k events) — note getSafeLinkUrl does NOT prevent this: a malformed-but-parseable URL (a second https:// → repeated //) passes validation and <Link>'s router still rejects it. PR #15245 renders these as a native <a> (which never runs the router's href validation), removing the family at the source — no new filter needed. (The older inline beforeSend invalid-href check — which never fired anyway, wrong field, Rule 6 — was removed in #15276.)

  6. Every beforeSend / denylist rule: narrow signature + written rationale + a NEGATIVE fixture proving a real error still passes. This is the MANDATORY review gate — for any suppression change, answer "does this rule hide a real error?" in the PR, and add a test asserting a real 5xx / unknown code / thrown error is NOT dropped (the sentryFilters.clienttest.ts pattern from #15145 / #15238). ⚠ Read the right event field: message-type events (console captures, benign strings) carry their text on event.message / event.logentry.message, NOT event.exception.values[0].value — a filter that only reads the exception value silently never fires on them (the reason the original invalid-href filter never worked).

  7. PII / HIPAA — never put user content in a message, extra, breadcrumb, or tag. No prompt text, trace content, tokens, share-link secrets, user/session ids. This is not hygiene — it is the compliance boundary: ALL cloud regions, including HIPAA, report to the same US Sentry org, and error events are never masked, so this discipline is the only thing keeping user content out of them. Session Replay is the one masked channel: instrumentation-client.ts gates maskAllText/blockAllMedia so replays are fully masked everywhere except the EU/US non-HIPAA cloud regions. Never remove or weaken that gate — the gate (isEuOrUsRegionNonHipaa → the replayIntegration options) must be covered by a CI regression test asserting the masked/unmasked regions on the real module (the mask-guard instrumentation-client-replay-mask.clienttest.ts; landing with #15802). Reject any diff that touches the gate without that guard passing, and run every diff touching instrumentation-client.ts or replay config through the reviewer checklist in the reference. A message that interpolates user data both leaks and shatters grouping (Rule 5).

  8. VERIFY in the environment that actually fires the error. Router/console validations run only in the real client runtime, not jsdom — a green unit test does NOT prove the console error is gone. Reproduce it in a browser against the running app (Playwright), do an A/B, and confirm the event disappears. ⚠ PR #15245 was verified this way — its native <a> fired 0 where the prior <Link> fired the error ×12. Unit tests lock the contract; the browser proves the noise removal.

Workflow

  1. Classify (tiny). For the error path, name the outcome — expected / transport / our-code — and write the one-line decision. That IS the PR note.
  2. Wire it. Expected → render UX (no capture). Transport → degrade (no capture). Real → captureException a real Error via the helpers, with an area tag, static message, structured extra, no PII.
  3. If suppressing: add a named predicate in sentryFilters.ts reading the right event field, with a written rationale and a negative fixture (Rule 6). Prefer root-causing the emission instead (Rule 5).
  4. Verify — unit test for the contract; browser A/B for the actual event removal (Rule 8).
  5. PR with the decision note; for suppression changes, the explicit "does this hide a real error?" answer + the negative-fixture test.

Anti-patterns

  • Capturing an expected 404 / permission / validation / malformed-user-input state (Rule 1).
  • captureException(nonError) or console.error(object) → opaque fingerprint (Rules 2, 3).
  • Re-reporting transport/infra failures the server already owns (Rule 4).
  • Adding a beforeSend filter with no rationale, no negative fixture, or one that reads the wrong event field (Rule 6).
  • Interpolating ids/user data into the message → PII + grouping explosion (Rules 5, 7).
  • Removing/weakening the region-gated replay mask, or "fixing" the mask-guard test instead of the diff that tripped it (Rule 7).
  • Trusting a green jsdom test as proof the noise is gone (Rule 8).
  • An ad-hoc inline beforeSend check instead of a named, tested predicate in sentryFilters.ts.

References

  • references/sentry-capture-contract.md — the capture contract in depth: the shared-helper APIs, the PII/HIPAA compliance boundary (replay mask + reviewer checklist), the beforeSend / denylist authoring protocol (field-reading, negative fixtures), fingerprinting and area tagging, the known noise families, and the shipped-PR case studies (#15145 / #15173 / #15174 / #15175 / #15238 / #15243 / #15245). Read when authoring a suppression rule, hardening a capture path, touching replay/region config, or triaging a family.

Frequently asked questions about Sentry Instrumentation

Similar skills