App Crashes on Launch: Missing NSXxxUsageDescription Fix

iOS kills the app the instant a privacy-protected API runs without a matching Info.plist usage description. Read the exact missing key from the device console, add it, confirm it survives the archive, and ship.

You tap Scan Receipt, the app freezes for one frame, then vanishes. No crash dialog, no error toast, just the home screen. Connect the device to a Mac, open Console.app, and you’ll see a line like “This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app’s Info.plist must contain an NSCameraUsageDescription key with a string value explaining to the user how the app uses this data.” iOS does not throw a recoverable exception here; it terminates the process immediately with SIGABRT from the TCC (Transparency, Consent, and Control) layer.

Fastest fix: that console line names the exact key you’re missing. Add it to your Info.plist (or, on modern Xcode, the INFOPLIST_KEY_* build setting) with a one-sentence, user-facing description, then rebuild. The whole repair is one string. The work is (1) reading the right key off the console, (2) writing a description App Review will accept, and (3) confirming the key actually survives into your archived build, because the most painful version of this bug is a key that exists in source but gets stripped from the TestFlight build.

Two separate “privacy” rejections get confused. A missing usage description crashes the app at runtime (this article). A missing or incomplete PrivacyInfo.xcprivacy manifest does not crash anything; it blocks the upload at App Store Connect with an email about “required reason API.” If your app runs fine but the upload is rejected, see the privacy-manifest note near the end.

Which key am I missing?

The console line names the key directly, so start there. If you can’t reach the console yet, map the feature you tapped to the key it requires (verified against Apple’s Information Property List reference, as of June 2026):

Feature that crashedRequired Info.plist key(s)
Camera capture / AVCaptureDeviceNSCameraUsageDescription
Microphone / audio recordingNSMicrophoneUsageDescription
Read photo libraryNSPhotoLibraryUsageDescription
Save to photo library onlyNSPhotoLibraryAddUsageDescription
Location while in useNSLocationWhenInUseUsageDescription
Location always / backgroundNSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription
IDFA / ad attribution (ATT)NSUserTrackingUsageDescription
Bluetooth (central or peripheral)NSBluetoothAlwaysUsageDescription
Local network (Bonjour, sockets)NSLocalNetworkUsageDescription (plus NSBonjourServices for Bonjour)
ContactsNSContactsUsageDescription
Calendar / RemindersNSCalendarsUsageDescription / NSRemindersUsageDescription
HealthKit read / writeNSHealthShareUsageDescription / NSHealthUpdateUsageDescription
HomeKitNSHomeKitUsageDescription
Motion / pedometerNSMotionUsageDescription
Speech recognitionNSSpeechRecognitionUsageDescription
Face IDNSFaceIDUsageDescription

Common causes

Listed by which API surface most often catches teams.

1. Camera / microphone / photo library access without the right key

Calling AVCaptureDevice.requestAccess(for: .video), PHPhotoLibrary.requestAuthorization, or instantiating UIImagePickerController with .camera source type — without NSCameraUsageDescription, NSMicrophoneUsageDescription, or NSPhotoLibraryUsageDescription — terminates the process.

How to spot it: Crash log shows TCC (Transparency, Consent, Control) framework in the trace; console line names the missing key explicitly.

2. Location used without NSLocationWhenInUseUsageDescription

CLLocationManager.requestWhenInUseAuthorization() requires NSLocationWhenInUseUsageDescription. If you also call requestAlwaysAuthorization(), you need both NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription since iOS 11.

How to spot it: Console: “Trying to start MapKit location updates without prompting for location authorization.” or app dies on the first locationManager.startUpdatingLocation().

3. Tracking via IDFA without NSUserTrackingUsageDescription (iOS 14+)

ATTrackingManager.requestTrackingAuthorization requires NSUserTrackingUsageDescription. Without it, the call hangs or returns .denied silently; in some SDKs (Facebook, Branch) it triggers a hard crash during init.

How to spot it: First-launch crash when AppDelegate calls Settings.shared.advertiserTrackingEnabled = true or similar SDK init code.

4. Bluetooth without NSBluetoothAlwaysUsageDescription

Instantiating CBCentralManager or CBPeripheralManager requires NSBluetoothAlwaysUsageDescription (iOS 13+). The old NSBluetoothPeripheralUsageDescription is deprecated; setting only the old key still crashes on iOS 13+.

How to spot it: Crash log mentions CoreBluetooth and the key name in the console.

5. Local network access on iOS 14+

Any non-trivial socket call (NWConnection, multipeer connectivity, Bonjour browsing) requires NSLocalNetworkUsageDescription. Apps that worked fine on iOS 13 started crashing or silently dropping connections on iOS 14.

How to spot it: Network feature works on iOS 13 simulator but on iOS 14+ device shows “App would like to find and connect to devices on your local network” alert; if Info.plist key missing, alert never shows and connection silently times out.

6. Contacts / Calendar / Reminders / Health / HomeKit / Motion

Each protected resource has its own key: NSContactsUsageDescription, NSCalendarsUsageDescription, NSRemindersUsageDescription, NSHealthShareUsageDescription, NSHealthUpdateUsageDescription, NSHomeKitUsageDescription, NSMotionUsageDescription. Crash trace always names the missing key in the console.

How to spot it: Console: “This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app’s Info.plist must contain an <KEY>…“

7. The key is set in source-controlled Info.plist but stripped from the archive

Most painful: the project compiles, runs on the simulator, but crashes on the TestFlight build. Build settings (Custom Info.plist file path, conditional builds, scripts that regenerate Info.plist) can strip keys at archive time.

How to spot it: Local Debug build works; only Release / Distribution archive crashes. Extracting Info.plist from the .ipa shows the key is missing.

Before you start

  • Reproduce the crash on a device (not just the simulator) — some protected APIs return canned values on the simulator and never crash.
  • Symbolicate the crash log or watch device Console.app live so you can read the explicit error string iOS prints — it names the missing key.
  • Note whether the issue appears only in Release builds; if so, look at build settings, not source.

Information to collect

  • The exact crash line from Console.app — copy the entire “This app has crashed because…” sentence.
  • The output of grep -E "NS[A-Z][A-Za-z]+UsageDescription" Info.plist for every target’s Info.plist.
  • The exact API call that triggered the crash (camera, location, ATT, Bluetooth, etc.).
  • A list of every SDK that initializes at launch — many request permissions in their init.
  • Build settings: INFOPLIST_FILE, INFOPLIST_PREPROCESS, any custom build phases that touch Info.plist.

Step-by-step fix

Step 1: Read the exact missing key from console

Plug the device in, open Console.app on Mac, filter by your app name, reproduce the crash. The line is unambiguous:

This app has crashed because it attempted to access privacy-sensitive data
without a usage description. The app's Info.plist must contain an
NSCameraUsageDescription key with a string value explaining to the user
how the app uses this data.

Treat this as authoritative — it names exactly which key to add.

Step 2: Add the key with a user-facing, specific description

Open the App target’s Info.plist (or INFOPLIST_KEY_* build settings in modern Xcode). Add the key:

<key>NSCameraUsageDescription</key>
<string>Scan receipts and barcodes to add items to your expense list.</string>

Description rules App Review enforces:

  • Must be specific (not “for camera access” — must say what user benefit).
  • Must be in the user’s locale; provide InfoPlist.strings for each supported language.
  • Should not say “may” / “might” — describe actual use.
  • Must match what your app actually does at the moment of the prompt.

Step 3: Verify the key reaches the archive

This is the step most teams skip, and it is where the “works locally, crashes on TestFlight” bug hides. The compiled Info.plist inside the archive is the only source of truth. (Note: the old xcrun PackageApplication command you may find in older answers was removed back in Xcode 8.3 — do not use it.)

If you exported an .ipa, read its Info.plist directly:

unzip -p YourApp.ipa "Payload/*.app/Info.plist" \
  | plutil -p - \
  | grep UsageDescription

If you only have the .xcarchive, point plutil straight at the compiled plist inside it (no export needed):

plutil -p \
  ~/Library/Developer/Xcode/Archives/*/YourApp\ */Products/Applications/YourApp.app/Info.plist \
  | grep UsageDescription

Or, by hand: in Xcode’s Organizer, right-click the archive, choose Show in Finder, then Show Package Contents on the .xcarchive and open Products/Applications/YourApp.app/Info.plist. If the key is in your source but absent here, your build settings or a build phase script is stripping it — go fix that, not the source.

Step 4: If your project uses modern Xcode (no Info.plist file)

Since Xcode 13/14, new projects ship with GENERATE_INFOPLIST_FILE = YES and no standalone Info.plist file. This is still the default in current Xcode (Xcode 26, as of June 2026). Each usage key maps to a build setting prefixed INFOPLIST_KEY_. Add it in Build Settings (search for the key name) or paste into the .xcconfig:

INFOPLIST_KEY_NSCameraUsageDescription = Scan receipts and barcodes to add items to your expense list.

The easier route: open the target’s Info tab, click +, and add a “Privacy - Camera Usage Description” row. Either way the build system synthesizes the Info.plist at compile time, so Step 3’s archive check is exactly how you confirm it landed.

Step 5: Localize the description for every supported language

In Info.plist, the English string is the fallback. For each locale, add InfoPlist.strings:

// en.lproj/InfoPlist.strings
"NSCameraUsageDescription" = "Scan receipts and barcodes to add items to your expense list.";

// zh-Hans.lproj/InfoPlist.strings
"NSCameraUsageDescription" = "扫描小票和条码,将物品添加到你的支出列表。";

// es.lproj/InfoPlist.strings
"NSCameraUsageDescription" = "Escanea recibos y códigos de barras para agregar artículos a tu lista de gastos.";

Without localized strings, App Review can reject for “non-English speakers see English text in a system permission alert.”

Step 6: Audit every SDK for hidden permission requests

SDKs request permissions on init, so a key you “don’t use” can still be required:

nm YourApp.app/YourApp | grep -E "requestAuthorization|requestAccess|requestTracking"

Or check release notes for SDKs you ship. Common offenders: Facebook SDK (ATT), Branch (ATT, location), Firebase Messaging (notifications), Adjust (ATT), audio/voice SDKs (microphone). Every permission an SDK touches needs a usage description in your Info.plist even if your own code never calls that API.

While you’re in the SDK list, check the separate-but-related privacy-manifest requirement (see the section below) — it won’t crash your app, but it will block the upload.

Step 7: Add a missing-key CI check

A 5-line script in your CI pipeline prevents this regression:

REQUIRED_KEYS="NSCameraUsageDescription NSPhotoLibraryUsageDescription NSLocationWhenInUseUsageDescription"
for key in $REQUIRED_KEYS; do
  /usr/libexec/PlistBuddy -c "Print :$key" "$APP_PATH/Info.plist" \
    || { echo "Missing $key"; exit 1; }
done

Run this against the archived .app, not the source Info.plist, so build-setting drift is caught.

Verify

  • Cold-launch the app on a real device, exercise the feature that previously crashed; the system permission alert appears with your description text.
  • Decline the permission; the app handles it gracefully (no crash, shows fallback UI).
  • Re-launch in a different language locale; the description text appears in that locale.
  • Crash log from yesterday’s TestFlight build no longer reproduces on the new build.
  • CI check passes for archived build.

Not the same thing: the privacy manifest (PrivacyInfo.xcprivacy)

If your app runs fine but the upload gets rejected, you’re hitting a different rule and a usage description won’t fix it. Since May 1, 2024, App Store Connect rejects uploads that call a “required reason” API (for example UserDefaults, file timestamps, disk space, or systemUptime) without declaring it in a PrivacyInfo.xcprivacy privacy-manifest file. The rejection arrives as an email from App Store Connect naming the API category (NSPrivacyAccessedAPICategoryUserDefaults and similar), not a runtime crash.

Two key differences:

  • A usage description is a runtime gate. Missing it crashes the app the moment the API runs.
  • A privacy manifest is a submission gate. Missing it blocks the upload at App Store Connect, but the app still runs in development.

Many third-party SDKs (you’ll see Apple’s “commonly used SDKs” list flagged during upload) must ship their own signed PrivacyInfo.xcprivacy. If your upload is blocked because of an SDK, update that SDK to a version that bundles its manifest. See Apple’s privacy manifest files and describing use of required reason API docs for the exact category strings and approved reasons.

Long-term prevention

  • Maintain a per-target Info.plist checklist of every usage key your code paths require; review on every release.
  • Treat new SDK additions as triggering a usage-description review; the SDK’s docs always list which permissions it touches.
  • Localize every usage description into every shipped language; use a CI step that fails if InfoPlist.strings is missing a key.
  • Wire a runtime check at first launch in DEBUG that asserts each key exists; surface it as a fatal error in dev so you catch missing keys before release.
  • Pin the build’s archived Info.plist as a build artifact; compare across releases to catch silent drops.
  • For complex apps, write integration tests that hit each permission-gated feature on a fresh simulator (no prior consents); they catch missing keys immediately.

Common pitfalls

  • Editing the Info.plist string directly in the system permission alert preview — that is a runtime display, not the source of truth. Change source Info.plist.
  • Using NSLocationAlwaysUsageDescription alone on iOS 11+; iOS requires NSLocationWhenInUseUsageDescription plus NSLocationAlwaysAndWhenInUseUsageDescription for always-authorization apps.
  • Vague descriptions like “We need camera access.” App Review rejects this — be specific.
  • Setting the key in the Watch target but not the iOS App target (or vice versa) when both call the API.
  • Removing the key in a refactor without realizing the SDK that previously triggered it is still bundled — crash returns on next release.
  • Assuming the simulator catches this — simulator returns mock data for many APIs, never triggering the missing-key crash.

FAQ

Q: Console says I need NSCameraUsageDescription, but I do not use the camera. Why?

A bundled SDK does. Run grep -RE "AVCapture|UIImagePicker|requestAccess.*video" Pods/ to find it. You either add the description for that SDK, or remove the SDK if you do not need that feature.

Q: Can I provide an empty string for the description?

No. App Review rejects empty or single-word strings. Apple recommends at least one sentence explaining how the app uses the data.

Q: My Watch app crashed but I added the key to the phone app’s Info.plist.

Watch apps have their own Info.plist. Add the key to the Watch target’s Info.plist as well. Same applies to App Clips, Widgets, and any framework that initializes a CLLocationManager standalone.

Q: Why does the simulator not crash for me?

Simulator’s TCC enforcement is lenient for some APIs; in particular, AVCaptureDevice.requestAccess may auto-return on the simulator without checking Info.plist. Always test on a real device. See TestFlight Build Missing on Device when device testing setup is the issue.

Q: My App Review rejection says “the purpose string is too vague.” What is acceptable?

The reviewer wants you to name the feature: “To scan receipts for expense tracking,” not “For camera access.” Reference the specific in-app feature that uses the data.

Q: My app runs fine but App Store Connect rejects the upload for privacy. Is this the same bug?

No. A missing usage description crashes the app at runtime; an upload rejected for “required reason API” is the separate PrivacyInfo.xcprivacy privacy-manifest rule that has applied since May 1, 2024. The email names an API category like NSPrivacyAccessedAPICategoryUserDefaults. Add or update the privacy manifest (often by bumping the offending SDK) rather than touching usage descriptions.

Q: I only added the camera key but the console now asks for the microphone too.

UIImagePickerController and AVCaptureSession video recording pull in audio, so the system checks both NSCameraUsageDescription and NSMicrophoneUsageDescription. Add both keys when you capture video.

Tags: #Troubleshooting #ios #info-plist #privacy #crash