Crash Triage for Unfamiliar Mobile Codebases
How to stabilize a vibe coded app when you did not write it from scratch
Crashes in TestFlight (Apple's beta testing) or Google Play but the codebase is AI built or unfamiliar? You do not need to understand every file to make progress. Reproduce the crash reliably, read the top crash groups, fix store-vs-test build differences first, then stop the bleeding before you consider rewriting anything.
Published June 2026 by Batteries Included
When this applies
- App built with Cursor, Lovable, Bolt, Replit, or similar AI tools
- Inherited project from a contractor or co-founder you no longer have access to
- “Works in the test build / simulator, crashes in TestFlight or the store-ready build”
- Every AI fix introduces a new crash
If you are still on the pre-beta side of things (trying to harden the code before crashes happen), that is the demo-to-production hardening article. This article is for when crashes are already happening and you need to stop them.
Store-ready crashes vs test-build-only crashes
This is the first question to answer because it changes where you look.
The version you submit to the app store goes through code compression and shrinking (ProGuard and R8 on Android). Your day-to-day test build does not. A crash that only shows up in TestFlight or Google Play internal testing is often caused by one of three things:
- Shrinking removed code the app still needs: a piece of code referenced indirectly gets stripped out during compression.
- Missing or wrong settings for the store build: the store-ready version points at a different server address, is missing a key, or skips setup steps that your test build ran.
- Unhandled situations: the happy path worked in the simulator or emulator, but real users hit cases the AI-generated code never exercised.
Our store release checklist covers store build mechanics, signing, and the mapping files that translate compressed crash reports back into readable form. Come back to it when you need store-specific help; here we focus on finding and fixing the crashes themselves.
Step 1: Reproduce on purpose
“It crashed once” is not useful. A reproducible crash is a fixable crash.
- Write down the minimum steps: open app → log in → tap X → crash. If you cannot write it down, you have not reproduced it yet.
- If the crash is intermittent, try different accounts, network conditions (slow connection, offline mid-flow), and fresh installs. Switching away from the app and back, or running on a phone low on memory, are common triggers in AI-generated apps that never tested those situations.
iPhone
- Match the exact build type. TestFlight crashes need a store-ready build from Xcode (Apple's app editor), not the everyday test build you run while coding.
- Match the device and operating system (OS) version from the crash report. A crash on iOS 16 on a real iPhone may not appear in the iOS 18 simulator.
- A store-ready build installed on a physical iPhone is usually the fastest way to reproduce a TestFlight crash without waiting for another TestFlight upload.
Android
- Match the exact build type. Google Play internal or closed testing crashes need a store-ready release build from Android Studio — usually with code compression (ProGuard/R8) turned on — not the everyday debug build you run from the Run button.
- Match the device and Android version from the Play Console crash report. A crash on a Samsung phone running Android 12 may not appear on a Pixel emulator running Android 14.
- Installing the same internal-testing build on a physical Android phone is usually faster than waiting for another Play upload. If you can build locally, install the release variant on a connected phone through Android Studio.
Step 2: Collect the right signals
iOS
- Xcode Crashes Organizer: for TestFlight and App Store builds, open Xcode → Window → Organizer → Crashes. Xcode turns raw memory addresses into readable function names and line numbers automatically if you have the matching saved build and debug symbol (dSYM) files. Apple's guide to readable crash reports explains what to do when that translation is incomplete.
- TestFlight feedback: testers can submit screenshots and comments; check App Store Connect under your build.
- Device logs: for crashes on a connected device, Xcode → Window → Devices and Simulators → View Device Logs.
Keep every Xcode build you distribute. Deleting saved builds means you lose the debug symbol (dSYM) files needed to read crash reports.
Android
- Play Console → Android vitals → Crashes and Application Not Responding (ANR) reports: the platform groups crashes by stack trace (the list of functions active when the app failed) and shows affected user counts. Google's Android vitals documentation explains the metrics. The key one for store discoverability is user-perceived crash rate, the percentage of daily active users who experienced at least one crash while the app was in the foreground. Google defines a bad-behavior threshold at 1.09% overall and 8% per device model; exceeding it reduces your app's visibility in Play search.
- Mapping file for readable stacks: if ProGuard or R8 compression is on, crash reports start as scrambled names. Android vitals shows decoded stack traces only after the mapping file is linked to that release—not when you upload it there. Most modern Android apps ship an Android App Bundle (AAB) with Android Gradle Plugin (AGP) 4.1 or later; in that case Play automatically pulls the deobfuscation file from the bundle. Check Test and release → App bundle explorer → Downloads → Assets to confirm it is attached. Manual upload is mainly for APK releases (or older build setups): same path, upload
mapping.txtin the Assets section. Google Play Console: deobfuscate crash stack traces - Device logs: for crashes you can reproduce locally, Android Debug Bridge (
adb logcat) captures the full stack at crash time in readable form.
Third-party crash reporters (optional)
If the app already has Firebase Crashlytics or Sentry integrated, use it; they group crashes by stack, track affected user counts over time, and let you mark issues resolved. If nothing is integrated yet, the platform tools above are enough to start sorting crashes. Do not spend time adding a crash reporter before you have fixed the top groups.
What any useful crash report needs: a stack trace (where in the code the app failed), app version, operating system (OS) version, device model, and (ideally) a short log of the last few actions before the crash.
Step 3: Rank crashes: fix order, not fix count
You will not fix everything. Rank by impact, then fix in order.
Fix first:
- Crashes on your core flow (signup, login, checkout, or whatever the app's one job is). These block every user.
- Crashes that appear across many users with the same stack trace (Play Console groups them; Crashlytics groups by signature). One root cause, many affected users: high leverage.
- New breaks: crashes that appeared after a specific AI change. Check your version history and compare to your last known stable build.
Fix later (or not at all):
- Edge screens rarely reached in normal use.
- One-off crashes on obscure device/OS combinations with no reproducible stack.
- Crashes from users on very old OS versions below your stated minimum.
User-perceived crash rate on Android is what Google measures for store visibility, so it is a good proxy for prioritization: crashes while the user is actively in the app count more than background crashes.
Step 4: Find the problem in AI-generated code without reading all of it
The goal is to locate the broken section, not review the whole project.
Use version history if you have it. If you know a build from two weeks ago was stable and today's is not, git bisect (a tool that binary-searches your change history) can find the exact change that introduced the crash. This works even in code you did not write.
Search for the name in the crash report, not the root cause. Take the top entry from the stack trace and search the codebase for that function name or file. Ignore everything else for now.
Look for copy-pasted patterns in high-risk areas. AI tools generate repetitive code. Login flows, server request handlers, and error handlers are often duplicated across screens with slight variations, and bugs in one copy often appear in all of them. A crash in one screen's login check probably means the same check is broken on every other screen using the same pattern.
Find “touch once, break twice” sections. If the same file or section appears in every crash report (a networking layer, a session manager, shared screen logic), that section is the one to fix first. It is also the section to freeze: once it is working, mark it hands-off in your next AI session.
Decide small fix vs focused rewrite. For a broken 40-line function, patch it. For a login section that is fundamentally wrong and appears in every crash, rewriting just that section is often faster than fixing every place that calls it. Keep the rewrite scoped: one section, clear inputs and outputs, easy to verify.
Stop the prompt-and-patch spiral. The most common failure pattern in inherited AI codebases: reproduce a crash → ask AI to fix it → new crash → repeat. Before running another prompt, reproduce the crash, confirm you understand the stack, and write a minimal test or manual repro step that fails. Fix to make that repro pass. Then run the prompt if needed. This forces each fix to be verifiable.
Step 5: Know when you are stable enough
You are not aiming for zero crashes; you are aiming for stable enough to ship and iterate.
Stable enough means:
- Core flow (login → main action → result) runs without crashing on a store-ready build on a physical device.
- No top-priority crash groups (crashes affecting a large share of your daily users) in Android vitals or your crash reporter.
- TestFlight or Play internal testing feedback is not about crashes on the main path.
Once you are there, the next work is hardening (automated checks, stricter code rules, frozen sections) so the next AI session does not re-introduce what you just fixed. That is the demo-to-production hardening article. After hardening, the store release checklist covers signing, privacy declarations, and upload steps.
When self-serve is enough vs when to get help
Self-serve works if:
- You can reproduce the crash locally on a store-ready build.
- The stack trace points at code you can find and read, even if you did not write it.
- Single platform, limited crash groups, and each fix does not create two new ones.
Get help if:
- Store-only crashes you cannot reproduce locally; the build setup itself may be the issue.
- Crash reports you cannot decode into readable form, or missing debug symbol (dSYM) files you cannot recover.
- Login or data bugs where you cannot tell if user A can see user B's data.
- The list of crashes to fix grows faster than the list of fixes; every session produces more crashes than it closes.
The two-to-three day Minimum Viable Product (MVP) Unblock Triage on Vibe coded app rescue is designed for exactly this: we go through the crash groups, identify root causes, and hand you a prioritized fix list, either to execute yourself or continue with us through Launch Rescue (two to four weeks, hands-on through TestFlight or Google Play).
Ready to stop the crash spiral?
Tell us the platform, the crash patterns, and whether you can reproduce them locally. We will tell you if Minimum Viable Product (MVP) Unblock Triage (2–3 days) or Launch Rescue fits, or point you back to the self-serve guides below.
Related
Sources
- Apple Developer Documentation: Adding identifiable symbol names to a crash report: iOS crash reports with debug symbol (dSYM) files and the Crashes Organizer.
- Android Developers: Crashes (Android vitals): user-perceived crash rate definition and bad-behavior thresholds.
- Android Developers: Android vitals: core vitals metrics and how they affect Play store visibility.
- Google Play Console Help: Deobfuscate or symbolicate crash stack traces: automatic mapping for app bundles with Android Gradle Plugin (AGP) 4.1+, manual upload via App bundle explorer, and viewing decoded traces in Android vitals.
- Google Play Console Help: Monitor your app's technical quality with Android vitals: crash rate metrics and cluster grouping.
- Firebase Crashlytics documentation: optional third-party crash reporting for iOS and Android.