●MAX — Rork Max handles code signing and provisioning so you can submit to the App Store in two clicks, with no local Mac required●SIMULATOR — Try your app in an in-browser streaming simulator, then scan a QR code to install straight to your device without TestFlight●NATIVE — Coverage reaches SwiftUI, ARKit, HealthKit, HomeKit, Core ML, and Metal, going where React Native cannot●SEED — Rork raised a $15M seed led by Left Lane Capital in April 2026, joined by Peak XV and a16z Speedrun●GROWTH — Reported at 743,000 monthly visits with 85% growth, widening access to AI-built mobile apps●NOCODE — Gartner expects 75% of new applications to be built with low-code or no-code tools by the end of 2026●MAX — Rork Max handles code signing and provisioning so you can submit to the App Store in two clicks, with no local Mac required●SIMULATOR — Try your app in an in-browser streaming simulator, then scan a QR code to install straight to your device without TestFlight●NATIVE — Coverage reaches SwiftUI, ARKit, HealthKit, HomeKit, Core ML, and Metal, going where React Native cannot●SEED — Rork raised a $15M seed led by Left Lane Capital in April 2026, joined by Peak XV and a16z Speedrun●GROWTH — Reported at 743,000 monthly visits with 85% growth, widening access to AI-built mobile apps●NOCODE — Gartner expects 75% of new applications to be built with low-code or no-code tools by the end of 2026
When Sentry Burned Through Its Event Quota in Days — Trimming Noise Before It Ships
A Sentry quota that empties early in the month is almost always a noise problem, not an error surge. Here is how to shrink event volume before it ships — with beforeSend, sampling, and grouping — while keeping the errors that actually matter inside the quota.
The morning after I shipped a new build, an email from Sentry was waiting: "You have used 80% of your event quota this month." It was July 3rd.
Errors had not spiked. The app was running fine. But the events flowing in had quietly filled up with the same harmless exception, fired thousands of times.
The free tier is 5,000 events per month. At that pace, one of my apps was on track to hit 18,000. Once the quota is gone, the crashes I actually need to see get dropped and never recorded. Monitoring in place, blind exactly when it matters. That was the outcome I wanted to avoid.
What follows is how I bring Sentry event volume under control for a Rork (React Native / Expo) app — before events ship, not after. Not by lowering the sample rate across the board, but by cutting noise on purpose while guaranteeing the important errors stay inside the quota. This is the line I draw in code, and the steps I used to measure volume and pull it back.
The quota shrinks regardless of severity
The first thing to internalize: Sentry's quota does not distinguish an "important error" from a "throwaway log." Both count as one event.
A fatal crash that earns a one-star review and a one-off AbortError from a user reloading on a flaky train connection are identical on the quota ledger. And the annoying part is that the high-volume one is almost always the latter.
Event type
Business importance
Typical volume
Hard crash / unhandled exception
High
Low
Transient network errors (AbortError, timeouts)
Low to medium
Very high
Third-party SDK internal warnings
Low
High
Performance traces (from tracesSampleRate)
Depends
Huge if misconfigured
When I grouped events by exception type in Discover, a single AbortError — a fetch aborted when the user left the screen — accounted for roughly 61% of the month's volume. That is not a crash. It is exactly the kind of thing you catch and swallow.
So the job is not to lower the sample rate uniformly. It is to target that 61% and drop it, while letting everything else through untouched.
First, see what is eating the quota in one query
Measure before you act. Lower the sample rate without identifying the culprit and you dilute the important errors too.
In Discover (or by sorting Issues), scope to the last 7 days and order by event count. Look at three things.
Signal
What it tells you
events per issue
How much of the quota one issue consumes. If the top three cover most of it, target those three
events / users ratio
Occurrences per user. An extreme ratio suggests a loop or runaway retry
transaction volume
If performance traces rather than errors are eating the quota, this is large
If an issue shows "hundreds per user," a retry loop is probably spinning in the background. That is a signal to stop the loop itself before you trim any noise. Network-layer design overlaps with the thinking in designing UX and error states under an unstable network.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦How to find what is eating your Sentry quota (with a real case where one AbortError was 61% of all volume)
✦Working code that combines beforeSend, sampleRate, ignoreErrors, and fingerprint to keep only the errors worth seeing
✦An allowlist guard that always lets critical errors through after sampling, plus a weekly review routine
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Before the code, separate two settings that are easy to confuse. Mix them up and you think you have throttled errors while traces keep draining the quota.
Setting
Applies to
How to think about it
sampleRate
Error events
Keep at 1.0 as a rule (send all errors). Drop specific noise individually in beforeSend
tracesSampleRate
Performance traces
0.05 to 0.1 in production. Leaving this at 1.0 burns the quota fast
Half of my first quota meltdown came from shipping with tracesSampleRate: 1.0 left over from development. Traces, not errors, were eating the budget. Rather than lower the error sampleRate, drop only the noise surgically in beforeSend. That division of labor is the starting point.
import * as Sentry from "@sentry/react-native";Sentry.init({ dsn: "https://YOUR_KEY@o0.ingest.sentry.io/YOUR_PROJECT", environment: __DEV__ ? "development" : "production", release: "app@1.4.0", // Send all errors as a rule; drop noise individually in beforeSend sampleRate: 1.0, // Throttle performance traces hard in production (a common quota drain) tracesSampleRate: __DEV__ ? 1.0 : 0.05, // Never even send known harmless messages ignoreErrors: [ "AbortError", "Network request failed", "TypeError: Network request failed", /Non-Error promise rejection captured/, ],});
ignoreErrors filters by message via strings or regex — the simplest entry point. But on its own it also drops network errors you may actually want. That is why we move on to beforeSend, where you can decide conditionally.
Drop it client-side in beforeSend
beforeSend is a hook that runs right before an event is sent to Sentry. Return null and the event is never sent. The advantage is real: you discard it before it consumes any quota.
Write the conditions for noise you are willing to swallow in meaningful units.
Sentry.init({ // ...settings above... beforeSend(event, hint) { const error = hint?.originalException; const message = (typeof error === "string" ? error : error?.message) || ""; // 1. A fetch aborted because the user left the screen is harmless if (error?.name === "AbortError") { return null; } // 2. Offline-driven network failures are frequent and self-explanatory, // but keep non-offline network failures, so scope the condition if ( message.includes("Network request failed") && event.contexts?.app?.in_foreground === false ) { return null; } // 3. Internal warnings from a specific third-party SDK you cannot fix const frames = event.exception?.values?.[0]?.stacktrace?.frames || []; const fromNoisySdk = frames.some((f) => (f.filename || "").includes("some-analytics-sdk") ); if (fromNoisySdk) { return null; } return event; },});
The point is to treat beforeSend not as "the place to reduce volume uniformly" but as "the place to write down why something is harmless." Drop it because it is an AbortError; drop it because it is a network failure while not in the foreground. When the reason is spelled out, the decision stays safe to reread later.
Loading unhandled exceptions from the React component tree into Sentry pairs well with the Error Boundary and unhandled promise design, so capture and suppression work together.
Group persistent noise with fingerprint
Some events are not worth deleting outright, yet arrive in bulk. For those, collapse them into one issue rather than dropping them. Sentry splits by stack trace differences, but a fingerprint forces grouping.
beforeSend(event, hint) { const error = hint?.originalException; const message = error?.message || ""; // 5xx tends to fragment into many issues via query-string or request-id // differences. Collapse to one per endpoint if (message.startsWith("HTTP 5")) { const endpoint = (error?.url || "unknown").split("?")[0]; event.fingerprint = ["http-5xx", endpoint]; } return event;}
Grouping itself does not directly cut quota usage. But when 100 fragmented issues collapse into one, "what is actually high-volume" becomes obvious at a glance. That makes the next sampling or beforeSend target clear, and the decision to stop wasteful sends comes faster.
Crashlytics has no cap, but that is not license to send everything
One cost note. Firebase Crashlytics has no event limit, so raw volume is not a concern. That part is relaxing.
But if you keep "unlimited means send it all," the dashboard fills with noise instead. Record non-fatals unconditionally with recordError() and the fatal crashes sink into a sea of counts.
import crashlytics from "@react-native-firebase/crashlytics";// Tag non-fatals with a severity attribute so you can filter laterfunction recordNonFatal(error, severity = "warning") { crashlytics().setAttribute("severity", severity); crashlytics().recordError(error);}
Even without a cap, the stance is the same: judge severity before you record. Limit or no limit, not sending noise is what protects your own visibility. This classification thinking is continuous with running actionable crash reports and staged distribution.
Always let critical errors through after sampling
The scariest part of trimming noise is dropping an important error along with it. To prevent that, I put an "always send" guard at the very top of beforeSend.
const ALWAYS_SEND = new Set([ "PaymentError", // billing can never be missed "AuthTokenMissing", // auth failure stops the user completely "DataCorruption", // corrupted saved data breaks trust]);beforeSend(event, hint) { const error = hint?.originalException; // allowlisted names pass unconditionally, before any suppression rule if (error?.name && ALWAYS_SEND.has(error.name)) { return event; } // suppression rules for AbortError etc. go below // ... return event;}
Design suppression as "when in doubt, keep it." Only drop what you can state, with reasons, is harmless. Anything you hesitate over, send. Prioritize not missing errors over protecting the quota. As long as that ordering holds, sampling stays a safe tool.
⚠️
Lowering `sampleRate` to something like 0.5 to thin errors uniformly means a low-frequency fatal bug can be "sampled out and never recorded once." Keep uniform error sampling as a last resort; drop noise by name in beforeSend first.
The numbers after rolling it out (field notes)
I ran this on one app for about two weeks. As an indie developer, I make every change myself and observe it myself.
Monthly sends were on pace for about 18,000 before the changes. Breaking it down in Discover, AbortError was roughly 61%, and traces from tracesSampleRate: 1.0 were the other pillar of pressure.
After lowering tracesSampleRate to 0.05 and dropping AbortError plus non-foreground network failures in beforeSend, the pace fell to about 3,200 events per month — a little over 60% of the 5,000 free tier.
What mattered came next: I deliberately triggered a payment error in staging and confirmed it passed the ALWAYS_SEND allowlist and reached Sentry. Noise down, fatal errors still through — you can only claim you protected the quota once both are verified. Not relaxing just because the number dropped is, I think, the biggest trap in this kind of work.
ℹ️
Noise patterns shift with every release. Add a new SDK or change your navigation design, and a different transient error rises to the top. Once a week I glance at the top issues in Discover and add one or two lines to the `beforeSend` suppression rules — a light habit, not a project.
Turn it into a weekly triage
Finally, the minimal routine so this does not stay a one-off. Keep it coarse enough to run in five minutes.
Cadence
Do this
Criterion
Weekly
Check top events over the last 7 days in Discover
Act if the top 3 exceed 50% of total
On the spot
Add one line to beforeSend for an issue you can call harmless
Can you state, with reasons, why it is harmless?
At release
Verify tracesSampleRate and the allowlist
Production at or below 0.1; billing/auth in the allowlist
Monthly
Deliberately trigger a fatal error in staging
Does it reach Sentry via the allowlist?
The goal of monitoring is not to record a lot; it is to have the error you need still there when you need it. Trimming noise before it ships is a quiet but effective way to keep that state inside a free tier.
Open your own project's Discover and find the single highest-volume issue over the last 7 days. If it is harmless, one line in beforeSend will change your consumption immediately. I hope this helps with your own setup. Thank you for reading.
Share
Thank You for Reading
Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.