If App Store Connect bounced your upload with a red ITMS-90683 banner that mentions a missing purpose string, the build did not fail for a code-signing or metadata issue. It failed because Apple's automated check found a call to a privacy-sensitive system API without a matching reason in Info.plist. The fix is one or two short XML entries; the trap is figuring out which SDK pulled the API in.
Short answer
ITMS-90683 means your binary calls a sensitive system API, almost always location, camera, contacts, or motion, and Info.plist does not declare why. Add the matching NSLocation*UsageDescription, NSCameraUsageDescription, or NSContactsUsageDescription key with a short human-readable reason, rebuild, and resubmit. According to Apple's documentation for requesting Core Location authorization, apps that ever call requestAlwaysAuthorization must include both NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription, or App Store Connect rejects the upload at submission time.
What you should know
- The check runs at upload time, not at runtime. It is symbol-based, so the missing key is flagged whether or not your app reaches that API in practice.
- The key list is fixed. Apple publishes every NSXxxUsageDescription string in the Information Property List reference. Invented keys do not satisfy the check.
- Location is the most frequent offender. requestAlwaysAuthorization needs both NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription paired together.
- Third-party SDKs are the usual cause. Stripe card-scan, OneSignal, Firebase Analytics, AppsFlyer, and Branch have all triggered location warnings in apps that never call Core Location directly.
- The string is user-facing. Apple shows your exact text in the iOS permission dialog. A meaningless placeholder ("App needs location") can pass the upload check and still trigger a Guideline 5.1.1 rejection from the human reviewer.
What does the 'Missing Purpose String' message actually mean?
The short answer is that ITMS-90683 is the build-time form of a permission audit Apple runs against the linker output of your IPA. The check looks for symbols in your binary that map to APIs guarded by a permission prompt: location, camera, microphone, contacts, photo library, calendar, reminders, Bluetooth, motion, speech recognition, Face ID, HealthKit, HomeKit, Siri, App Tracking Transparency. Each one has a fixed Info.plist key. If the symbol appears in the linked binary and the matching key is absent, the upload is rejected with ITMS-90683 and the category name.
The message itself has a stable shape: "Missing purpose string in Info.plist. Your app's code references one or more APIs that access sensitive user data, or the app has one or more entitlements that permit such access. The Info.plist file should contain a NSLocationAlwaysAndWhenInUseUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data." That last sentence varies by category and is the only reliable signal of which key to add. According to Apple's reference page for NSLocationAlwaysAndWhenInUseUsageDescription, the string is mandatory whenever any code path in the binary can request always authorization, even one that the user can never reach in normal use.
The check is automated and lives inside App Store Connect's upload pipeline. It runs before TestFlight processing, and it ignores Info.plist entries that exist only in build variants you did not submit. Submitting the Release configuration matters; the Debug Info.plist does not count for the upload that gets reviewed.
Which Info.plist keys does iOS require for location access today?
Two keys for location-always, one for location-while-in-use. iOS 11 and later require NSLocationWhenInUseUsageDescription for any foreground location request, paired with NSLocationAlwaysAndWhenInUseUsageDescription whenever the app ever escalates to always authorization. NSLocationAlwaysUsageDescription is the iOS 10 and earlier name; modern builds rarely need it, but the App Store Connect check still flags it for builds that target older deployment versions or that ship a Core Location SDK compiled against an older base SDK.
Apple's requesting authorization to use location services page sets the rule clearly: if your code calls requestWhenInUseAuthorization(), include NSLocationWhenInUseUsageDescription. If your code ever calls requestAlwaysAuthorization(), include both NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription. The system shows the when-in-use string first; the always string appears later if and when the prompt escalates.
| Permission scope | Required Info.plist key(s) | Applies to |
|---|---|---|
| Foreground only | NSLocationWhenInUseUsageDescription | iOS 8 and later |
| Always (modern) | NSLocationWhenInUseUsageDescription plus NSLocationAlwaysAndWhenInUseUsageDescription | iOS 11 and later |
| Always (legacy) | NSLocationAlwaysUsageDescription | iOS 10 and earlier |
| Background updates | One of the above, plus the Background Modes capability with Location updates checked | All versions |
How do you find the SDK that triggered the missing purpose string?
The trap with ITMS-90683 is that grep over your source tree usually returns nothing. The call lives inside a compiled framework binary, often shipped as a static archive or an XCFramework with no source. Three commands cover most cases.
First, run grep -r "NSLocation" Pods/ (or node_modules for React Native, or your Plugins folder for Flutter) to find any framework that mentions a location key in its bundled Info.plist or its source. Many SDKs document their permission needs by shipping an example Info.plist snippet inside the package.
Second, run nm -gU on each .framework or .a inside the build artifact and pipe through grep -i location. The Core Location symbols that App Store Connect checks for include _CLLocationManager, _requestAlwaysAuthorization, _requestWhenInUseAuthorization, and _startMonitoringSignificantLocationChanges. If any of these appear in a third-party binary, your Info.plist needs the matching key, whether or not your own code calls them.
Third, search the issue trackers. The pattern is well documented: Firebase Analytics (pre-10.x), OneSignal (specifically on Flutter and Cordova), Branch, AppsFlyer, and the Stripe iOS SDK (because the card-scanning module links Core Location through the scanner) have each generated ITMS-90683 reports for apps that did not call Core Location directly. Pinning these SDKs to a version that ships a privacy manifest declaring the access often clears the warning at the next upload.
What does an acceptable purpose string look like to App Review?
Apple's App Review Guideline 5.1.1 on Data Collection and Storage requires the string to clearly and completely describe the data use. In practice, that means three things. The sentence is a complete English sentence. It names the feature that needs the data. It names the data itself ("your location", "your camera"). Vague strings like "Permission required" or copies of the key name fail human review under 5.1.1 even when the build passes ITMS-90683 at upload.
A working example for a delivery app:
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app uses your location to show couriers near you and route your order. Location data leaves the device only when you place an order.</string>
That string passes both checks: the upload scanner sees the key exists, and the reviewer sees a sentence that maps to a feature in the binary. Builders that auto-generate the string ("App requires access to location") often pass the upload check and then catch a 5.1.1 rejection at human review. Rewrite those strings before submission.
Why does the same build that passed last month suddenly fail?
Two reasons, both visible on the developer forums. First, the long-running Apple Developer Forums thread on ITMS-90683 shows that Apple's symbol scanner updates without announcement. A symbol that was not flagged in 2023 can be flagged in 2026 once Apple adds it to the watched list. Second, dependency updates pull new code paths. A minor-version bump of Firebase or Branch can introduce a Core Location call that did not exist in the previous version. Lock dependencies, diff the framework binaries between releases, and run nm against the artifact before each App Store Connect upload.
A third reason matters for no-code builders. FlutterFlow, Bubble, Thunkable, and Adalo each ship their own iOS shells, and each release bundles a different set of native plugins. A build exported from FlutterFlow in May 2026 may include a location plugin that the same project did not include in February 2026, and ITMS-90683 fires only on the new build. Open the generated Info.plist from the exported project and confirm which keys are declared before resubmitting.
What to watch out for
The first trap is declaring every key to be safe. Apple's Guideline 5.1.1 reviewer flags purpose strings for APIs the app does not actually use, because the description is then false. Match each key to a real code path. If a dependency is the only reason a key exists, the string should describe the dependency feature ("Card scanning checks your card photo, which can access location metadata"), not invent a feature your app does not have.
The second trap is shipping a placeholder string. The text is user-facing on the iOS permission prompt. Strings like "Test", "Required", or "App needs location" are accepted by the upload scanner and rejected by the human reviewer. Rewrite them to one or two complete sentences naming the feature and the data.
The third trap is targeting an older deployment version. Builds with iOS 10 still in the deployment target may need NSLocationAlwaysUsageDescription as well as the modern keys. Raise the deployment target to iOS 13 or later if there is no specific reason to support iOS 10. The OWASP MASVS privacy controls also recommend declaring only the minimum data set the app actually uses, not a superset that exists "just in case".
For builders who want an external automated read of their compiled IPA's Info.plist and permission usage before they submit to App Store Connect, PTKD.com (https://ptkd.com) is one of the platforms focused specifically on pre-submission scanning aligned with OWASP MASVS for no-code and vibe-coded apps. The scanner flags missing purpose strings, missing privacy manifest entries, and SDK-induced API access that the developer may not realize the binary contains. A scan is faster than a TestFlight round-trip when the goal is to confirm Info.plist coverage before the next upload attempt.
Key takeaways
- ITMS-90683 is a symbol-based check. Adding the right Info.plist key, not changing source code, is the fix in almost every case.
- For Core Location, ship NSLocationWhenInUseUsageDescription as a minimum; add NSLocationAlwaysAndWhenInUseUsageDescription whenever any code path in the binary can request always authorization.
- The cause is usually a third-party SDK that links Core Location internally. Pin SDK versions, run
nmon framework binaries, and search issue trackers before guessing. - The user-facing string is reviewed twice. The upload scanner only checks that the key exists; the human reviewer under Guideline 5.1.1 checks that the sentence is honest.
- Some teams scan compiled IPAs through external platforms like PTKD.com (https://ptkd.com) before each App Store Connect upload to catch missing permission descriptions, missing privacy manifest entries, and SDK-pulled APIs the developer did not realize are in the binary.



