getsentry / getsentry/sentry-react-native
Keep the native session's error count correct when a JS error is dropped by sampling
- Dominant language
- TypeScript
- Stars
- 1.8k
- Forks
- 366
- Avg merge
- 1d 9h
- Merged PRs (30d)
- 89
Description
### Summary
When an error event is dropped **by sampling** on the JS side (`sampleRate`), the envelope is never forwarded to native, so the native session never records that an error occurred — a session that should be `errored` (or `unhandled`) can finalize as healthy/`exited`. Adopt `updateSessionForDroppedEventNonTerminating` to update the native session's error count in that path, without sending an envelope.
This is the **sampling-path counterpart to #6659** (which handles unhandled errors that *do* produce an envelope). It is the RN equivalent of Flutter [#4008](https://github.com/getsentry/sentry-dart/pull/4008).
Targeted at the **next RN SDK major** and **stacked after #6659** — see *Sequencing* below.
### The drift
RN sessions are owned by the native SDK; JS forwards envelopes. The only native session signal is via `captureEnvelope` (`wrapper.ts:196-235` → `RNSentryModuleImpl.java:500-511`). So when JS drops an error by `sampleRate` before it reaches `captureEnvelope`, native never learns an error occurred, and the session's `errors` count / status drifts. Same motivation as Flutter #4008: without a separate update, a gracefully ending session is reported as `exited` instead of `errored`/`unhandled`.
### Scope — sampling only, NOT `beforeSend`
Call this **only for events dropped by sampling**. The native API contract is explicit (sentry-cocoa `SentryInternalEnvelopeApi`):
> "Do not call this for events dropped by `beforeSend` or ignored exception types, and do not call it in addition to `captureNonTerminating` for the same event."
So this path must fire for `sampleRate` drops only — not `beforeSend` returning `null`, not ignored/denied exception types. Flutter #4008 does the same: it "receives only final events accepted by processors and `beforeSend`." (This corrects the original issue text, which mentioned `beforeSend`.)
The API takes an `unhandled: boolean` — set it from the dropped event's `mechanism.handled === false`.
### Native API (available now)
- **Android** — sentry-java 8.55.0 ([#5990](https://github.com/getsentry/sentry-java/pull/5990)), merged via #6658: `InternalSentrySdk.updateSessionForDroppedEventNonTerminating`.
- **iOS** — sentry-cocoa 9.27.0 (pinned in `RNSentry.podspec`): `SentrySDK.internal.envelope.updateSessionForDroppedEventNonTerminating(unhandled:)`.
### Design sketch
- **JS** — a "sampled-out event" hook in the client path: when an error event is dropped specifically by `sampleRate` (not `beforeSend`/ignored), call a new `NATIVE` method with the `unhandled` flag. Mirror Flutter #4008's internal lifecycle-hook design once it settles.
- **Bridge** — new bridge method `updateSessionForDroppedEventNonTerminating(unhandled: boolean)` (additive, backward-compatible; older cached native binaries simply no-op it).
- **Android / iOS** — forward to the respective native API; catch at the boundary, off-main where the native call persists.
- **Guard** — never call this for an event that also went through the #6659 capture path (double-count).
### Sequencing
- **Stacked after #6659.** Shares the non-terminating session plumbing and the no-double-count rule; #6659's capture path is the stable base.
- **Hold on the JS hook design** until Flutter #4008 (still open/unmerged) settles — it's the novel part most likely to be reshaped in review.
- **Same milestone as #6659** (next major).
### Release classification
Not an API/ABI break (additive bridge method, no public JS API change). It **is** a Release Health behavior shift — `errored`/`unhandled` session rates become more accurate under error sampling — so it belongs in the next major with a CHANGELOG note.
Follow-up from the 8.55.0 bump (#6658). Pairs with #6659.
Contributor guide
Research direction
Start with the sampled-event client path and the existing bridge flow in wrapper.ts:196-235 and RNSentryModuleImpl.java:500-511, then review the stacked #6659 plumbing and RNSentry.podspec. Confirm the native Android and iOS forwarding APIs before settling the JS hook design. Done means sampled error drops update the native session with the mechanism's unhandled value, while beforeSend drops, ignored types, and already-captured events do not double-count or send an envelope.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, ios, react-native, typescript
- Domain
- mobile, observability
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100