Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions domains/observability/knowledge/error-reporting-paths.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
domain: observability
title: Error reporting paths
---

# Error Reporting Paths

What reaches Sentry when something throws, and what a `catch` does to that.

Read at `metamask-extension` [`a5fc11920`](https://github.com/MetaMask/metamask-extension/commit/a5fc11920).

## An uncaught throw already reports

`Sentry.init` in [`app/scripts/lib/setupSentry.ts`](https://github.com/MetaMask/metamask-extension/blob/a5fc11920/app/scripts/lib/setupSentry.ts#L199) passes an `integrations` array and never sets `defaultIntegrations: false`. A grep for `defaultIntegrations` across `app/` and `shared/` returns nothing, so the browser SDK merges its own defaults alongside the listed ones.

That default list includes `globalHandlersIntegration()`, which hooks `onerror` and `onunhandledrejection`. So an uncaught throw and an unhandled rejection both produce a Sentry event with no code at the throw site doing anything.

## Therefore the two states are reported and silent

Adding `.catch(console.debug)` to a path that previously threw does not degrade a feature gracefully. It deletes the only report. The pair is not crash versus degrade, it is reported versus silent, and the second state has no signal anywhere.

The shape that keeps both is a catch that reports:

```ts
import { captureException } from '../../../shared/lib/sentry';

somethingAsync().catch((error) => {
captureException(error);
});
```

`captureException` is exported at [`shared/lib/sentry.ts:18`](https://github.com/MetaMask/metamask-extension/blob/a5fc11920/shared/lib/sentry.ts#L18) and is already the house value-import at `background.js`, `metamask-controller.js`, `offscreen.ts` and the migrations.

## A reported-but-swallowed path is still quiet

Reporting is not the same as being noticed. A change inside `try { … } catch { captureException(e); return; }` degrades a feature without crashing: nothing goes red, no test fails, and the only signal is an error-tracker entry nobody is watching. The same edit in a hot path is caught in minutes.

Weight findings by observability rather than by likelihood alone. **An unlikely failure in a swallowed path can outrank a likely one in a loud path.**

## Not established

Whether the MV3 service worker's global scope hooks these handlers the way the window scope does. `globalHandlersIntegration` is written against `window` events, and the worker has no `window`. Settling it needs a runtime check in the worker context rather than a reading of the SDK source, and until then the reported-versus-silent claim above is verified for page contexts only.

## Related

- `typescript` domain `skills/tsc-blindspots/skill.md`, "Silent failure modes deserve their own pass" — the same weighting applied to reviewing a diff.
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,11 @@ grep -rn "excludeMetaMetricsId: true" app/ ui/ shared/ --include="*.ts" --includ

- Slack: `#metamask-metametrics`
- Team: `@consensys/data-council`

## Swallowing an Error Deletes Its Report

`Sentry.init` here never sets `defaultIntegrations: false`, so the browser SDK merges `globalHandlersIntegration()` in and an uncaught throw or unhandled rejection already reports on its own.

A `.catch(console.debug)` added to such a path is not graceful degradation. The two states are reported and silent, and the shape that keeps both is a catch calling `captureException` from `shared/lib/sentry`.

Mechanism, sources and the one unestablished case: `knowledge/error-reporting-paths.md`.
34 changes: 15 additions & 19 deletions domains/typescript/skills/tsc-blindspots/skill.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
---
name: tsc-blindspots
description: >-
Find the type defects `tsc` is structurally unable to report — a green build is
not evidence the types are correct. Covers the two classes: (1) hand-written
types that restate an authoritative source and disagree with it, caught by
substituting the derived type at a fixed commit and diffing `tsc` output; and
(2) the standing blind spots in the language and config — unchecked array/record
indexing, bivariant method parameters, covariant arrays, `any` absorption at
untyped boundaries, precise signatures fed `any` at every call site, ambient
`declare module` assertions that launder an `any` into a confident type,
excess-property checks that only fire on fresh literals, and external data
asserted rather than validated. Also audits typing edits that quietly change
runtime behavior:
stripped `| undefined`, deleted default parameters, literals swapped for runtime
enum lookups, calls made optional so a throw becomes a silent no-op. Use when
reviewing a JS→TS migration, a PR that hand-writes types for values that already
have them, a "rename-only" refactor, or any PR claiming a change is mechanical.
Triggers on /mms-tsc-blindspots, or on phrases like "validate this TypeScript
migration", "is this type right", "does this type match the real shape",
"why didn't CI catch this type", "derive vs define", and "what can tsc not
check".
Find the type defects `tsc` is structurally unable to report: a green build is
not evidence the types are correct. Two classes. Hand-written types that restate
an authoritative source and disagree with it, caught by substituting the derived
type at a fixed commit and diffing `tsc` output. And the standing blind spots in
the language and config: unchecked indexing, bivariant method parameters,
covariant arrays, `any` absorption at untyped boundaries, ambient `declare
module` assertions that launder an `any`, excess-property checks that fire only
on fresh literals, and external data asserted rather than validated. Also audits
typing edits that quietly change runtime behavior: stripped `| undefined`,
deleted defaults, a call made optional so a throw becomes a silent no-op. Use
when reviewing a JS to TS migration, or any PR claiming a change is mechanical.
Triggers on /mms-tsc-blindspots, "is this type right", "derive vs define",
"what can tsc not check".
maturity: experimental
---

Expand Down Expand Up @@ -267,6 +261,8 @@ entry nobody is watching. The same edit in a hot path would be caught in minutes
Weight findings by observability, not just by likelihood: **an unlikely failure in a
swallowed path can outrank a likely one in a loud path.**

On `metamask-extension` the prior question is whether the path reported at all: an uncaught throw already does, so a `catch` that only logs removes the report rather than softening a crash. See observability domain `knowledge/error-reporting-paths.md`, delivered beside `instrumentation`.

## Why the build stays green regardless

- The hand-written type **compiles by construction** — that is why it was written.
Expand Down
Loading