Getting to 99.9% Crash-Free Sessions: A Crash Triage Playbook
React Native app crashing in production? A triage playbook: crash-free metrics, readable stack traces, common causes, prioritising fixes and safe releases.
Full-Stack, Mobile & AI Engineer
"99% crash-free" sounds excellent until you do the arithmetic. An app with 100,000 sessions a day at 99% crash-free sessions crashes 1,000 times a day. Each of those is someone whose checkout, workout log or message disappeared in front of them, and some of them will leave a one-star review that says exactly that.
Getting from "mostly stable" to 99.9% isn't about one heroic fix. It's a routine: crashes you can read, a clear way to decide which to fix first, knowledge of the usual causes, and releases that can't make things much worse. I run Sentry crash triage as part of the release path I own, including on an app with 100K+ active users at 99.9% crash-free sessions. This playbook covers what to do when a React Native app is crashing in production, from first visibility to a weekly routine.
Key takeaways
- Crash-free sessions and crash-free users measure different things; track both, per release.
- Readable stack traces come first: source maps for every build and update, plus iOS dSYMs and Android mapping files.
- Prioritise by users affected, trend and severity, not by whichever crash someone mentioned last.
- A handful of causes explain most React Native crashes: uncaught JavaScript errors, native module bugs, memory, ANRs and SDKs.
- Releases are your safety net: phased rollouts with crash gates, plus fast fixes and rollbacks for JavaScript issues.
What crash-free sessions actually measure
- Crash-free sessions: the share of app sessions that didn't end in a crash.
- Crash-free users: the share of users who didn't experience a crash in the period.
A crash that hits a few heavy users repeatedly hurts sessions more than users; a crash on launch for one device model hurts users. Watch both, broken down by release, because the question that matters most after every release is "is this version better or worse than the last one?"
| Crash-free sessions | Crashed sessions per 100,000 |
|---|---|
| 99% | 1,000 |
| 99.5% | 500 |
| 99.9% | 100 |
| 99.95% | 50 |
On Android, Google Play adds its own stakes. Android vitals sets "bad behavior thresholds" of 1.09% for the user-perceived crash rate and 0.47% for the user-perceived ANR rate overall, and 8% for either on a single phone model. Above them, "Play may reduce the visibility of your title" and may show users a warning on your store listing.
Step 1: make every crash readable
A crash report that says undefined is not an object at line 1 of a minified bundle is useless. Set up crash reporting so every report tells a story:
- JavaScript and native crashes in one place.
@sentry/react-nativecaptures both, with an Expo config plugin for managed projects. - Source maps for every build and every over-the-air update, tied to the exact release and build number, so stack traces point at your real code.
- Native symbols: dSYM files for iOS and R8 or ProGuard mapping files for Android, uploaded automatically in CI.
- Breadcrumbs: navigation events, network requests and key user actions leading up to the crash.
- Context without personal data: app version, OS, device model and an anonymous user ID, never names, emails or tokens.
import * as Sentry from "@sentry/react-native";
Sentry.init({
dsn: process.env.EXPO_PUBLIC_SENTRY_DSN,
environment: __DEV__ ? "development" : "production",
sendDefaultPii: false,
tracesSampleRate: 0.2,
});
export default Sentry.wrap(App);Check the setup by triggering a test crash in a release build and confirming the stack trace shows your file names and line numbers.
Step 2: triage like an on-call engineer
Not every crash deserves attention this week. Sort them:
| Factor | Higher priority when... |
|---|---|
| Users affected | Many users, or a growing share |
| Trend | New in the latest release, or regressed after being fixed |
| Severity | Crashes at launch, sign-in, checkout or while saving data |
| Concentration | Concentrated on one device model or OS version (often easy to fix) |
| Fixability | A clear stack trace and reproduction path |
A crash at launch affecting 0.5% of users beats a background crash affecting 3% of sessions for a few heavy users. Give each top issue an owner and a target release, and mark the rest as known with a reason.
Step 3: know the usual suspects
| Cause | What you see | Typical fix |
|---|---|---|
| Uncaught JavaScript error | App closes; JS stack trace in the report | Error boundaries around screens, defensive parsing of API data, typed responses |
| Native module bug | Native stack trace in a library | Update the library, check New Architecture compatibility, report upstream |
| Out of memory | Crash with no clear stack, often on image-heavy screens | Resize images, use list virtualisation, release caches |
| ANR (Android) | "App isn't responding" after the main thread is blocked | Move heavy work off the main thread, avoid large synchronous work at startup |
| Third-party SDK | Crash inside an SDK, often at launch | Update, lazy-initialise or remove the SDK |
| OS update | Spike on a new iOS or Android version | Test on beta OS versions before release day |
| Incompatible OTA update | Crash right after an update downloads | Use runtime versions so JS updates only reach compatible native builds |
Uncaught JavaScript errors deserve special attention, because in a release build an unhandled exception closes the app. Error boundaries turn a crash on one screen into a recoverable error message, and validating API responses at the edge stops a missing field from taking the whole app down.
Step 4: fix, verify, prevent
- Reproduce using the breadcrumbs, device model and OS version from the report.
- Write a regression test where it's practical, especially for data-handling crashes.
- Fix the cause, not the symptom. A try/catch that hides the crash without handling it moves the problem somewhere harder to see.
- Ship and verify: confirm the issue stops occurring in the new release before closing it.
- Prevent the class of bug: stricter types, a lint rule, a shared parser or a library update across the codebase.
Step 5: make releases a safety net
From the field: Release ownership is part of how I work: EAS Build, Submit and Update, Fastlane and GitHub Actions pipelines, phased rollouts, store review including rejections, and Sentry crash triage. On an app with 100K+ active users at 99.9% crash-free sessions, that release discipline matters as much as any single fix.
- Phased rollouts: release to a small share of users first and widen only when crash-free rates match or beat the previous version.
- Crash gates: agree in advance which numbers pause a rollout.
- Fast JavaScript fixes: over-the-air updates such as EAS Update can ship JS fixes quickly, within store rules for bug fixes, with rollback if an update misbehaves.
- Kill switches: feature flags let you turn off a crashing feature without a new build.
A weekly crash review
Thirty minutes a week keeps stability from drifting:
- Crash-free sessions and users for the current and previous release.
- New and regressed issues since last week.
- Top five issues by users affected, each with an owner.
- ANR rate and Android vitals status.
- Anything blocking a fix (can't reproduce, waiting on a library).
When the app "keeps stopping" or quits unexpectedly
Users don't report stack traces; they report symptoms. On Android, "App keeps stopping" means a crash, usually native or an unhandled JavaScript error; an "isn't responding" dialog is an ANR. On iOS, the app simply closes. To find the cause when crash reporting hasn't caught it:
- Android: reproduce with the device connected and read the log with
adb logcat, filtering for your package andFATAL EXCEPTION; Play Console's Android vitals also lists crashes and ANRs from real users. - iOS: open Xcode's Organizer for crash reports from TestFlight and App Store users, or the device's analytics logs for a single phone.
Then add whatever the crash reporter missed, often native crashes at startup before it initialises, to your setup.
FAQ
Why is my React Native app crashing in production but not in development?
Release builds behave differently: code is minified, development warnings are gone, an uncaught JavaScript error closes the app instead of showing a red screen, and real users have devices, OS versions, data and network conditions you haven't tested. Add crash reporting with source maps and breadcrumbs to see what's actually happening.
What is a good crash-free sessions rate?
Aim for 99.9% or better for a mature app. At 99%, an app with 100,000 daily sessions crashes 1,000 times a day. On Android, also keep the user-perceived crash rate under Google Play's 1.09% bad behaviour threshold and the ANR rate under 0.47%.
How do I get readable stack traces in React Native?
Upload source maps for every build and every over-the-air update, tied to the exact release, plus dSYM files for iOS and R8 or ProGuard mapping files for Android. Crash reporting tools such as Sentry then map minified traces back to your source code.
How do I prioritise which crashes to fix?
Rank by users affected, trend (new or regressed in the latest release) and severity (launch, sign-in, checkout or data loss first). Give each top issue an owner and a target release, and review the list weekly.
Next steps
If your app's crash rate is hurting reviews or Play Store visibility, I can set up crash reporting properly, triage the top issues and put release guardrails in place. Get in touch, or see my services.
Sources
- Android vitals, Android Developers
- Sentry for React Native, Sentry docs
- Release health, Sentry docs
- ANRs, Android Developers
- Runtime versions and updates, Expo documentation
Related
React Native Errors You'll Hit in 2026, and How to Fix Each
Common React Native errors and fixes: Metro, native modules, rendering, hooks, Hermes, iOS and Android builds, library upgrades, and how to debug the rest.
Debugging React Native in 2026: The Tools That Matter
Debugging React Native in 2026: React Native DevTools, logs on both platforms, native tools, network requests, release-only bugs and crash reports.
A Testing Strategy for React Native Apps That Teams Actually Keep
A practical React Native testing strategy: static checks, unit tests, React Native Testing Library v14 component tests, E2E flows and CI that sticks.
