# Denied permissions do not crash the app

- **Pattern:** `ab-001911` (`mobile-permissions-privacy.graceful-degradation.no-crash-on-denial`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001911
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001911)

## Why it matters

An unhandled promise rejection from a permission-gated API call causes a visible red screen in development and a silent crash in production — neither outcome is acceptable. CWE-391 (Unchecked Error Condition) and CWE-755 (Improper Handling of Exceptional Conditions) directly describe this pattern. Apps that call `Geolocation.getCurrentPosition()`, `Contacts.getAll()`, or `Camera.takePicture()` without a preceding permission check assume the permission is permanently granted — but users can revoke permissions mid-session in iOS and Android Settings. The revocation takes effect immediately: the next call to the native API will fail with an authorization error, and if the call is not wrapped in a try-catch, the app crashes. This is the leading cause of crash loops reported after users update their privacy settings.

## Severity rationale

Medium because a crash on permission denial is reproducible by any user who revokes a permission mid-session — a common action — causing complete feature unavailability and crash reports that surface in App Store reviews.

## Remediation

Wrap every protected API call in a try-catch and check permission status before the call. Never assume a previously granted permission is still in effect.

```typescript
import { check, PERMISSIONS, RESULTS } from 'react-native-permissions'
import Geolocation from '@react-native-community/geolocation'

async function getCurrentLocation(): Promise<{ lat: number; lng: number } | null> {
  try {
    const status = await check(
      Platform.OS === 'ios'
        ? PERMISSIONS.IOS.LOCATION_WHEN_IN_USE
        : PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION
    )
    if (status !== RESULTS.GRANTED) {
      return null // Feature degrades gracefully
    }
    return await new Promise((resolve, reject) =>
      Geolocation.getCurrentPosition(
        pos => resolve({ lat: pos.coords.latitude, lng: pos.coords.longitude }),
        reject,
        { timeout: 5000 }
      )
    )
  } catch (error) {
    console.warn('Location unavailable:', error)
    return null // Never throw — callers receive null and handle accordingly
  }
}
```

## Detection

- **ID:** `no-crash-on-denial`
- **Severity:** `medium`
- **What to look for:** Enumerate all permission-gated API calls (Geolocation, Camera, Contacts, etc.). For each, check whether a try-catch or error handler wraps the call. Count how many have defensive permission checks before the API call.
- **Pass criteria:** Zero permission-gated features crash when permissions are denied. At least 100% of protected API calls have try-catch or error handling that prevents crashes. App either disables features or shows clear error messages.
- **Fail criteria:** Any feature crashes or becomes unresponsive when a permission is denied. Any protected API call lacks error handling.
- **Skip (N/A) when:** App requests no permissions (no permission-gated API calls found).
- **Detail on fail:** Quote the unprotected API call. Example: `"App crashes with unhandled promise rejection when location permission denied in location tracking screen"`
- **Remediation:** Check permissions before accessing protected APIs:

  ```typescript
  import { check, PERMISSIONS, RESULTS } from 'react-native-permissions'

  async function getCurrentLocation() {
    try {
      const status = await check(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE)

      if (status === RESULTS.GRANTED) {
        return Geolocation.getCurrentPosition(/* callback */)
      } else {
        throw new Error('Location permission not granted')
      }
    } catch (error) {
      console.warn('Location unavailable:', error.message)
      return null
    }
  }
  ```

## External references

- cwe CWE-391
- cwe CWE-755
- iso-25010 reliability.fault-tolerance

Taxons: error-resilience

HTML version: https://auditbuffet.com/patterns/ab-001911
