App launches without crashing on supported OS versions
Why it matters
A crash on launch is the highest-severity rejection — the reviewer cannot evaluate any other part of the app. Apple tests on multiple OS versions and device types; an unhandled exception in root initialization code (unguarded useEffect, top-level JSON.parse, missing optional chaining) will crash on at least one configuration. CWE-390 (detection of error condition without action) and ISO 25010:2011 fault-tolerance both classify this as a fundamental reliability failure. One uncaught rejection in a startup async chain means the entire binary is rejected.
Severity rationale
Critical because a launch crash makes the app completely unreviable — no functionality can be evaluated and rejection is immediate.
Remediation
Add a root error boundary in App.tsx and wrap all startup async operations in try/catch.
// App.tsx
import { ErrorBoundary } from 'react-error-boundary';
export default function App() {
return (
<ErrorBoundary FallbackComponent={({ error }) => (
<View><Text>Something went wrong. Please restart the app.</Text></View>
)}>
<NavigationContainer>
<RootNavigator />
</NavigationContainer>
</ErrorBoundary>
);
}
For startup data fetching, always add a catch block: useEffect(() => { fetchConfig().catch(console.error); }, []). Test launch on the minimum supported OS version using Simulator (iOS) or AVD (Android) before every submission.
Detection
- ID:
no-crash-on-launch - Severity:
critical - What to look for: Count all relevant instances and enumerate each. Look for crash-causing patterns in the app entry point and early initialization code: unhandled promise rejections in
useEffecton the root component; missing null checks on environment variables that are expected at startup; synchronous throws in module-level code (top-levelJSON.parse()without try/catch, optional chaining missing on potentially undefined values); missing error boundaries at the root level. CheckApp.tsx(React Native/Expo),main.dart(Flutter),AppDelegate.swift(iOS), orMainActivity.kt(Android) for try/catch coverage of critical initialization. Also search for known crash-inducing patterns: callingnavigation.navigate()before the navigator is ready, accessingAsyncStoragesynchronously, or using deprecated APIs removed in the minimum supported OS version. - Pass criteria: The app entry point and initialization sequence have error handling. At least 1 implementation must be verified. A root-level
ErrorBoundary(React Native) or equivalent (FlutterErrorWidget.builder, iOSNSSetUncaughtExceptionHandler) is present to catch unexpected crashes gracefully. - Fail criteria: Missing error boundary at the root level; synchronous throws in module initialization; unhandled rejections on startup async operations; use of APIs deprecated or removed in the minimum supported OS.
- Skip (N/A) when: Never — launch stability is always required.
- Detail on fail:
"No root ErrorBoundary found — any uncaught exception will crash the app on launch"or"App.tsx has unhandled async initialization in useEffect with no catch block" - Remediation: Apple's review process runs your app on multiple OS versions and device types. Protect launch:
- Add a root error boundary in React Native:
// App.tsx import { ErrorBoundary } from 'react-error-boundary'; export default function App() { return ( <ErrorBoundary FallbackComponent={ErrorFallback}> <NavigationContainer>...</NavigationContainer> </ErrorBoundary> ); } - Wrap all startup
useEffectasync operations in try/catch - Use optional chaining (
?.) and nullish coalescing (??) defensively on all external data at startup - Test launch on the minimum supported OS version using Simulator (iOS) or AVD (Android)
- Add a root error boundary in React Native:
External references
- cwe · CWE-390 — Detection of Error Condition Without Action
- iso-25010:2011 · fault-tolerance — Fault Tolerance (ISO 25010 Reliability)
Taxons
History
- 2026-04-18·v1.0.0·Initial import from app-store-review-blockers·automated