Skip to content

fix(sentry): stop double-counting fetch failures, add missing DrawerTitle#2448

Open
innolope-dev wants to merge 1 commit into
mainfrom
fix/sentry-client-noise
Open

fix(sentry): stop double-counting fetch failures, add missing DrawerTitle#2448
innolope-dev wants to merge 1 commit into
mainfrom
fix/sentry-client-noise

Conversation

@innolope-dev

@innolope-dev innolope-dev commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Problem

Three of our top-volume Sentry issues are amplification, not new signal. captureConsoleIntegration({levels: ['error','warn']}) turns every console.error/console.warn into a Sentry event, and fetchWithSentry both captures the underlying failure at the call site and re-throws a ServiceUnavailableError wrapper that global handlers capture again. A single API timeout could produce up to three events:

  1. the explicit timeout captureException (kept — this is the real signal, e.g. PEANUT-UI-QHG)
  2. the re-thrown wrapper bubbling up — PEANUT-UI-QDJ, 1.6k events
  3. a consumer's console.error about the same failure — PEANUT-UI-MH5, 2.5k events

Separately, PEANUT-UI-MJS (1.7k events, 854 users) is Radix's DialogContent requires a DialogTitle a11y warning: BadgeStatusDrawer is the only drawer without a DrawerTitle.

Fix

  • sentry.utils.ts: add ServiceUnavailableError to the ignore list — it is always a wrapper created by fetchWithSentry whose cause was already captured with full request context at the fetch site.
  • fetchWithSentry: log the raw fetch rejection at console.info (not error) — the explicit captureException calls right below carry the full context; the error-level log only produced a duplicate event via captureConsole.
  • useTokenPrice: the "falling back to tokenDenomination" log becomes console.info — the fallback is graceful and the underlying failure is already reported. The captureException stays for genuinely new (non-fetch) errors; beforeSend now drops the wrapper case.
  • BadgeStatusDrawer: the badge name heading becomes the DrawerTitle (real a11y fix, not a suppression). Note: DrawerTitle adds leading-none tracking-tight from the shared component — minor typographic delta on this one-line title.

Deliberately NOT suppressed

  • The timeout captures themselves (/users/me, /rain/cards, /tokens/price) — 10s timeouts hitting 1.7k users is genuine API-latency signal.
  • Capacitor "plugin is not implemented" — a top-frame occurrence means a genuinely broken binary; PEANUT-UI-QV3's iframe source was already fixed in 972f354 and residual events are stale binaries (resolve in Sentry, don't filter in code).

Testing

  • pnpm typecheck clean, related jest tests green, prettier applied.

Summary by CodeRabbit

  • Bug Fixes

    • Improved error filtering to prevent duplicate reporting of service-unavailability errors.
    • Reduced duplicate Sentry events from handled network failures.
    • Clarified fallback logging for token price retrieval.
  • UI Improvements

    • Updated the badge status drawer header for improved accessibility and consistency.

…itle

captureConsoleIntegration promotes every console.error/warn into a Sentry
event, and fetchWithSentry both captures the underlying failure AND
re-throws a ServiceUnavailableError wrapper that gets captured again by
global handlers. One API timeout could produce three Sentry events.

- sentry.utils: ignore ServiceUnavailableError (always a wrapper whose
  cause was already captured at the fetch site) — PEANUT-UI-QDJ
- fetchWithSentry: console.info instead of console.error for the raw
  fetch rejection; the explicit captures below carry the full context
- useTokenPrice: fallback log at console.info — the fallback is graceful
  and the failure is already reported — PEANUT-UI-MH5
- BadgeStatusDrawer: badge name becomes the DrawerTitle, fixing the Radix
  'DialogContent requires a DialogTitle' a11y warning — PEANUT-UI-MJS
@vercel

vercel Bot commented Jul 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
peanut-wallet Ready Ready Preview, Comment Jul 18, 2026 7:53am

Request Review

@innolope-dev innolope-dev self-assigned this Jul 18, 2026
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The changes update shared Sentry filtering and console logging behavior, adjust token price error handling, and replace the badge drawer’s plain heading with DrawerTitle.

Changes

Sentry observability

Layer / File(s) Summary
Sentry filtering and fallback logging
sentry.utils.ts, src/utils/sentry.utils.ts
Adds ServiceUnavailableError to ignored patterns and changes handled network-failure logging from console.error to console.info.
Token price error handling
src/hooks/useTokenPrice.ts
Changes the token price fallback log to console.info while retaining explicit Sentry exception capture.

Badge drawer semantics

Layer / File(s) Summary
Badge drawer title
src/components/Badges/BadgeStatusDrawer.tsx
Renders displayName with DrawerTitle instead of a styled h1.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: hugo0

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: reducing duplicate Sentry fetch-failure captures and adding the missing DrawerTitle.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sentry-client-noise

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed: dependency version conflict. Check your lock file or package.json.


Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 6208.86 → 6209.3 (+0.44)
Findings: 0 net (+3 new, -3 resolved)

🆕 New findings (3)

  • medium high-mdd — src/utils/sentry.utils.ts:339 — fetchWithSentry: MDD 42.2 (uses across many lines from declarations)
  • medium high-mdd — src/components/Badges/BadgeStatusDrawer.tsx:26 — BadgeStatusDrawer: MDD 28.9 (uses across many lines from declarations)
  • medium complexity — src/hooks/useTokenPrice.ts — CC 18, MI 60.33, SLOC 69

✅ Resolved (3)

  • src/utils/sentry.utils.ts:339 — fetchWithSentry: MDD 40.8 (uses across many lines from declarations)
  • src/components/Badges/BadgeStatusDrawer.tsx:26 — BadgeStatusDrawer: MDD 27.9 (uses across many lines from declarations)
  • src/hooks/useTokenPrice.ts — CC 18, MI 60.31, SLOC 69

@github-actions

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 2000 ran, 0 failed, 0 skipped, 32.6s

📊 Coverage (unit)

metric %
statements 59.5%
branches 43.3%
functions 48.4%
lines 59.8%
⏱ 10 slowest test cases
time test
3.2s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
1.2s src/utils/__tests__/demo-api.test.ts › isDemoMode() is false when not running under Capacitor
0.4s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › every sticker stays within canvas at any count
0.3s src/app/actions/__tests__/api-headers-extended.test.ts › should not include apiKey in validateInviteCode body
0.3s src/app/(mobile-ui)/withdraw/__tests__/withdraw-states.test.tsx › Bank withdrawal keeps the $1 minimum for sub-$1 amounts
0.3s src/app/actions/__tests__/api-headers.test.ts › should include Content-Type in validateInviteCode
0.2s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › keeps stickers off the username pill (final pass respects the keep-out)
0.2s src/utils/__tests__/url.utils.test.ts › uses the public BASE_URL in Capacitor, not the localhost WebView origin
0.1s src/utils/__tests__/demo-balance.test.ts › auto-refills a wallet older than the TTL on cold start
0.1s src/utils/__tests__/demo-balance.test.ts › debits and floors at zero
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sentry.utils.ts`:
- Around line 41-46: Restrict the alreadyReported rule in sentry.utils.ts so
shouldIgnoreError ignores only the fetchWithSentry wrapper’s exact
ServiceUnavailableError exception type, not events whose message, value, type,
or culprit merely contains that text. Update the rule using the existing
error-pattern configuration mechanism and add regression coverage for both the
wrapper match and unrelated containing messages if tests are available.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 35a31120-3ad6-4871-8247-b7cc312339c4

📥 Commits

Reviewing files that changed from the base of the PR and between f9ab02d and 10ee160.

📒 Files selected for processing (4)
  • sentry.utils.ts
  • src/components/Badges/BadgeStatusDrawer.tsx
  • src/hooks/useTokenPrice.ts
  • src/utils/sentry.utils.ts

Comment thread sentry.utils.ts
Comment on lines +41 to +46
// fetchWithSentry wrapper errors: the underlying timeout/network/HTTP
// failure is already captured at the fetch site with full context, so the
// re-thrown ServiceUnavailableError bubbling to global handlers (or being
// console.error'd by a consumer) would only double-count it (PEANUT-UI-QDJ).
alreadyReported: ['ServiceUnavailableError'],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Narrow the ServiceUnavailableError ignore rule.

shouldIgnoreError applies patterns with substring matching across the message, exception value, type, and culprit. This suppresses unrelated events that merely mention ServiceUnavailableError, despite the comment describing an exact wrapper match. Match the exception type explicitly (or add regression tests covering both the wrapper and unrelated messages containing this text).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sentry.utils.ts` around lines 41 - 46, Restrict the alreadyReported rule in
sentry.utils.ts so shouldIgnoreError ignores only the fetchWithSentry wrapper’s
exact ServiceUnavailableError exception type, not events whose message, value,
type, or culprit merely contains that text. Update the rule using the existing
error-pattern configuration mechanism and add regression coverage for both the
wrapper match and unrelated containing messages if tests are available.

@innolope-dev

Copy link
Copy Markdown
Collaborator Author

Reconciliation note — this PR overlapped with two others opened later the same day; they've been rescoped so nothing conflicts:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant