Quality & Security· 7 min read

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.

By

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 sessionsCrashed 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-native captures 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:

FactorHigher priority when...
Users affectedMany users, or a growing share
TrendNew in the latest release, or regressed after being fixed
SeverityCrashes at launch, sign-in, checkout or while saving data
ConcentrationConcentrated on one device model or OS version (often easy to fix)
FixabilityA 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

CauseWhat you seeTypical fix
Uncaught JavaScript errorApp closes; JS stack trace in the reportError boundaries around screens, defensive parsing of API data, typed responses
Native module bugNative stack trace in a libraryUpdate the library, check New Architecture compatibility, report upstream
Out of memoryCrash with no clear stack, often on image-heavy screensResize images, use list virtualisation, release caches
ANR (Android)"App isn't responding" after the main thread is blockedMove heavy work off the main thread, avoid large synchronous work at startup
Third-party SDKCrash inside an SDK, often at launchUpdate, lazy-initialise or remove the SDK
OS updateSpike on a new iOS or Android versionTest on beta OS versions before release day
Incompatible OTA updateCrash right after an update downloadsUse 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

  1. Reproduce using the breadcrumbs, device model and OS version from the report.
  2. Write a regression test where it's practical, especially for data-handling crashes.
  3. Fix the cause, not the symptom. A try/catch that hides the crash without handling it moves the problem somewhere harder to see.
  4. Ship and verify: confirm the issue stops occurring in the new release before closing it.
  5. 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:

  1. Crash-free sessions and users for the current and previous release.
  2. New and regressed issues since last week.
  3. Top five issues by users affected, each with an owner.
  4. ANR rate and Android vitals status.
  5. 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 and FATAL 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

Bhuwanesh Sisodia

Bhuwanesh Sisodia

Full-Stack, Mobile & AI Engineer

Bhuwanesh Sisodia is a Full-Stack, Mobile & AI Engineer based in Mumbai, with 7+ years of experience and 50+ apps shipped to the App Store and Google Play. He works independently with startups and product teams worldwide.

About meGitHubLinkedInX

Related