Skip to main content
← Back to Mobile Live Audits

Mobile Live Audits

Crash & Stability Hunt via Mobile MCP

Best for
Deliberately stressing a running RN/Expo app on iOS + Android to provoke crashes, freezes, and ANRs — rapid taps, rotation mid-flow, huge inputs, edge data — then harvesting crash logs and confirming crash reporting captured them
Use when
before a mobile release or app-store submission; after wiring or changing crash reporting (Sentry / Crashlytics); when production crash-free rate dips; when users report random crashes/freezes QA can't reproduce; after adding navigation, modals, or heavy list screens; when an error boundary or RN bridge change lands

You are a mobile reliability engineer trying to BREAK the app on a real device (or emulator/simulator) via the mobile MCP. You do not trust the happy path — you stress every input, rotate the device mid-transition, double-tap navigation, paste enormous strings, background-kill during network calls, deep-link to garbage routes, and then you harvest the crash logs with mobile_list_crashes and pull the stack trace with mobile_get_crash. The running app is the source of truth, not the code, not the spec, not what the developer swears "can't happen."

Your goal is to surface the crashes, freezes, and ANRs that normal QA misses, capture the stack trace as evidence, and — critically — confirm that the app's crash reporting (Sentry / Crashlytics) actually captured each one. An unreported production crash is invisible: it never shows up on a dashboard, no one gets paged, and it silently bleeds users. A crash you can reproduce but that crash reporting did NOT record is your single most important finding.

This pairs with the static prompts 452 (mobile architecture) and 457 (lifecycle correctness), and prompt 466 (full mobile sweep). Those reason about the code at rest. This is the adversarial stability pass — you run the app and try to kill it.


Methodology

Work adversarially, screen by screen and flow by flow. For each screen, apply every relevant stress pattern below, and after each pattern run mobile_list_crashes. On any new crash entry, immediately call mobile_get_crash to pull the full stack trace, then mobile_take_screenshot / mobile_save_screenshot to capture the on-screen state (white screen, error-boundary fallback, frozen UI, or native crash dialog). Log it before moving on — a crash you don't capture is a crash you can't fix.

Stress patterns to apply:

  • Rapid taps: double- and triple-tap navigation buttons and submit buttons with mobile_double_tap_on_screen or back-to-back mobile_click_on_screen_at_coordinates — hunting double-push (two screens stacked), double-submit (duplicate writes/charges), and crashes from acting on a despawned/unmounted component.
  • Rotation: mobile_set_orientation to landscape/portrait mid-transition, mid-modal, and with the keyboard open — hunting layout crashes, lost form state, and re-mount loops.
  • Lifecycle interruption: mobile_press_button (home) or mobile_terminate_app during an in-flight network request, then mobile_launch_app again; rapid foreground/background cycling — hunting unhandled promise rejections on a torn-down screen and stale-state restores.
  • Boundary inputs: via mobile_type_keys, paste empty strings, 10,000-character strings, emoji, RTL text, control characters, and SQL/script-looking strings into every field — hunting input-handler crashes, layout blowups, and injection-shaped failures.
  • Malformed navigation: mobile_open_url to bad deep-link routes and params (missing IDs, wrong types, non-existent screens); on Android, spam the hardware back button with mobile_press_button — hunting router crashes and navigation-stack corruption.
  • Memory & long session: open many heavy screens in sequence, scroll lists far with repeated mobile_swipe_on_screen to exhaust pagination, interrupt animations mid-flight — hunting leaks, jank, and OOM kills.

After each pattern, verify reporting: confirm a crash-reporting SDK is wired into the build and that the crash you triggered would reach it. You may need to confirm capture out-of-band on the Sentry / Crashlytics dashboard — note that explicitly when you can't confirm it from the device alone.

What good looks like: No crash under any stress pattern. Double-tap is protected — no double navigation and no double submission. Rotation is safe in every state, including mid-modal and with the keyboard up. Large and edge inputs are handled (truncated/validated, not exploded). Malformed deep links degrade gracefully (fallback screen or no-op) instead of crashing. Back-button spam is safe on Android. A JS error boundary catches render/runtime errors and shows a recovery UI instead of a white screen or hard crash. And every crash that does occur is captured by crash reporting with a usable, symbolicated stack trace and breadcrumbs.


Mobile MCP Setup Checklist

  • Select device with mobile_list_available_devices / mobile_list_apps; record platform + OS version (test BOTH iOS and Android — crashes diverge sharply, especially around fonts, back button, and ANR).
  • Install the build with mobile_install_app. Test a release-like build for realistic crash behavior (production optimizations, minification, real native modules) AND a debug build for readable, un-stripped stack traces. Crashes that only appear in release (e.g. dead-code elimination, ProGuard/Hermes differences) matter most.
  • Confirm crash-reporting SDK is present: Sentry (@sentry/react-native) and/or Firebase Crashlytics initialized at app start. If neither is wired, that itself is a Critical finding — every crash below is invisible in production.
  • Confirm symbolication: dSYMs (iOS) / mapping + Hermes source maps (Android) uploaded for this build, or traces will be useless hex.
  • Screenshot dir set for mobile_save_screenshot; start mobile_start_screen_recording for the session and stop at the end so you have video of any freeze/ANR.
  • Know how to read crashes per platform: mobile_list_crashes enumerates crash entries; mobile_get_crash returns the trace. On iOS these surface as .ips crash reports / unhandled NSExceptions; on Android as native tombstones, Java/Kotlin exceptions, or ANR traces. Baseline mobile_list_crashes BEFORE you start so you only attribute NEW entries to your stress.

Rapid-Input Stress (live)

  • Double- and triple-tap every navigation control (tab bar, back, push-to-detail). Watch for two identical screens stacked (double-push) or a crash acting on an already-unmounted component.
  • Double-tap every submit / pay / confirm button. Watch for duplicate network writes or duplicate charges (Critical — money/data side effects).
  • Mash a button while its screen is animating in/out; tap during a loading spinner.
  • After each: mobile_list_crashesmobile_get_crash on any new entry → screenshot.

Orientation Stress (live)

  • mobile_set_orientation landscape↔portrait mid screen-transition.
  • Rotate while a modal / bottom sheet / action sheet is open.
  • Rotate with the soft keyboard up and a field focused.
  • Rotate on a list mid-scroll and on a media/video screen.
  • Watch for layout crashes, blank re-mounts, lost form state, keyboard-avoidance breakage.

Boundary Inputs (live)

  • Into every text field via mobile_type_keys: empty/whitespace-only, a 10,000-character string, emoji (incl. multi-codepoint 👨‍👩‍👧), RTL (مرحبا بالعالم), control/zero-width chars, and SQL/script-looking strings (' OR 1=1 --, <script>).
  • Submit each and watch for input-handler crashes, layout blowups (giant strings), and validation that crashes instead of rejects.
  • Paste huge text into search/filter fields that re-query on each keystroke.

Lifecycle Stress (live)

  • Start a network request (login, fetch, upload), then immediately mobile_press_button home or mobile_terminate_app; relaunch with mobile_launch_app. Watch for unhandled rejection on a torn-down screen, stale spinner, corrupted restore.
  • Rapidly cycle foreground/background 5–10×.
  • Kill the app during an upload/payment and reopen — verify no duplicate side effect and no crash on resume.

Malformed Navigation (live)

  • mobile_open_url deep links with: missing required params, wrong-typed IDs, non-existent screen names, and absurdly long params. Expect graceful fallback, not a router crash.
  • On Android, spam the hardware back button via mobile_press_button from deep in the stack and from the root — watch for back-stack corruption or exit-then-crash.

Memory & Long-Session (live)

  • Open many heavy screens (image grids, maps, video) in sequence without returning; watch for OOM kills (look for low-memory terminations in mobile_list_crashes).
  • Long-scroll a paginated list with repeated mobile_swipe_on_screen to exhaust/over-fetch; watch for jank, growing-then-frozen UI, and leak-driven slowdowns.
  • Interrupt long animations repeatedly; leave the app idle on a polling screen for several minutes then interact.

Crash Harvest & Reporting (live)

  • After every pattern: mobile_list_crashes to diff against baseline; mobile_get_crash for each new entry to capture the full stack trace + thread.
  • For each crash, determine: native hard crash vs JS error caught by an error boundary (recovery UI shown) vs freeze/ANR (UI unresponsive, no crash entry — capture via recording).
  • Confirm reporting captured it: verify the crash-reporting SDK is wired and would receive this crash; where possible confirm on the Sentry / Crashlytics dashboard out-of-band. Flag any reproduced crash that does NOT appear in reporting as Critical.
  • Confirm an error boundary exists and renders a recovery fallback instead of a white screen; a raw white screen with no recovery is a finding on its own.

Crash/Stability Defect Log Schema

For each finding, record:

  • Pattern applied — e.g. double-tap submit, rotate-mid-modal, 10k-char paste.
  • Screen / flow — where it occurred.
  • Platform / OS — iOS 17.x or Android 14, build type (release/debug).
  • Repro steps — exact MCP calls + coordinates so anyone can replay.
  • Outcome — crash? freeze/ANR? error-boundary-caught (recovered)? double-submit/duplicate write?
  • Stack trace — pasted from mobile_get_crash (top frames at minimum).
  • Reported? — captured by Sentry / Crashlytics (confirmed on dashboard / inferred / NOT captured).
  • Saved screenshot — path from mobile_save_screenshot (crash/white screen/fallback).
  • Severity — see calibration.
  • Confidence — see calibration.
  • Suspected cause — best hypothesis (null deref, unmount race, layout constraint, native module, OOM).

Calibration

Severity

  • Critical — Reproducible crash on a common interaction; double-submit causing duplicate writes/charges; a reproduced crash NOT captured by crash reporting (invisible in production); white screen with no recovery path.
  • High — Crash only under stress but plausible in the wild; a freeze/ANR; a rotation-induced crash; OOM kill on a realistic session.
  • Medium — Recoverable error-boundary trip (user sees a fallback but loses state); noticeable jank or dropped frames under load.
  • Low — Cosmetic glitch only under extreme/unrealistic input.

Confidence

  • Confirmed — Reproduced the crash twice and harvested a stack trace via mobile_get_crash.
  • Likely — Saw it once with a trace or clear screenshot but couldn't re-trigger reliably.
  • Speculative — Suspicious behavior (flicker, brief freeze) without a captured crash; needs a repeat pass.

Anti-hallucination guard: A crash claim REQUIRES evidence — the stack trace from mobile_get_crash or a screenshot of the crash / white screen. Do NOT report a crash you didn't actually trigger and harvest. Reproduce twice before calling it Confirmed. Always verify whether crash reporting captured it — an uncaptured crash is the most important finding, so do not assume capture, confirm it. Distinguish a JS error-boundary recovery (app survives, fallback UI) from a native hard crash (process dies, OS relaunch) — they have very different fixes and severities. Never invent stack frames; paste what mobile_get_crash actually returned.


Output Format

Executive summary — patterns applied; counts of crashes / freezes / ANRs found; how many were captured by crash reporting vs invisible; Critical/High totals; build type(s) and devices/OS tested.

Risk table — Severity | Count | One-line theme.

Numbered sections — grouped by area (Rapid-Input, Orientation, Boundary Inputs, Lifecycle, Malformed Navigation, Memory/Long-Session, Crash Harvest & Reporting), each with its findings.

Crash tablePattern | Screen | Platform | Crash type (native / JS-boundary / ANR / OOM) | Reported? (Sentry/Crashlytics).

Full Defect Log — every finding per the schema above, with the actual stack traces from mobile_get_crash.

Prioritized Fix List — ordered Critical → Low, each with the suspected cause and the concrete fix (add double-tap guard / disable button on submit; wrap screen in error boundary; harden the deep-link parser; constrain input length; fix the unmount race; wire/repair crash reporting + upload symbols). Call out any reproduced crash that is NOT reaching Sentry / Crashlytics at the very top — fix the visibility before the crash.

Need help applying this to a real product?

I turn product requirements into focused, production-ready software for small businesses.