Every TestFlight upload now greets you with a yellow banner: “Missing Compliance — Provide export compliance information.” Until you click through a multi-step questionnaire, the build stays in Waiting for Review for TestFlight and external testers cannot see it. This happens because your Info.plist does not include ITSAppUsesNonExemptEncryption, so App Store Connect cannot pre-answer the US export administration question on your behalf. The fix is one Info.plist key — but choosing the right value, and knowing when an annual self-classification (ERN) is actually required, is where teams get stuck.
Common causes
Ordered by what triggers the banner most often.
1. ITSAppUsesNonExemptEncryption simply not in Info.plist
If the key is absent, App Store Connect assumes “unknown” and prompts on every upload. New projects from older Xcode templates do not include it; SwiftUI App templates do not add it either.
How to spot it: grep ITSAppUsesNonExemptEncryption Info.plist returns nothing. The TestFlight build shows the yellow compliance warning immediately after processing finishes.
2. Key is set to true but you only use HTTPS / standard iOS APIs
Most apps only use URLSession (HTTPS), Keychain, or CryptoKit with stock algorithms — all of which qualify as exempt under US BIS 740.17(b)(1) and Apple’s “uses only exempt encryption” path. Marking the key true (non-exempt) when you do not ship custom crypto is the worst answer: it forces you to upload an ERN and a year-1 BIS notification you do not actually need.
How to spot it: Your code never imports OpenSSL, libsodium, BoringSSL, custom AES tables, or DRM modules. Everything cryptographic comes from Apple frameworks or HTTPS. If so, you are exempt — but the key currently says true.
3. Key is set to false but the app does ship non-exempt encryption
A subtle inverse case: you bundled a third-party VPN SDK, a custom file encryption library, or a DRM module. Marking false is a misrepresentation that can show up in audits.
How to spot it: Podfile.lock contains modules like OpenSSL-Universal, WireGuardKit, libsodium, BoringSSL-GRPC, or you have hand-rolled AES-GCM. The honest answer is true plus the exemption claim flow.
4. The key is set in the wrong target
Workspaces with App + Widget + Watch + Share Extension targets each have their own Info.plist. Setting it only on the App target while the Extension target is missing it can still trigger the warning on the parent app, depending on the App Store Connect version.
How to spot it: Running find . -name Info.plist then grep -l ITSAppUsesNonExemptEncryption across each match returns fewer entries than your target list.
5. CFBundleShortVersionString downgrade after fix
You added the key, but the new build’s CFBundleShortVersionString is older than a build already on App Store Connect. App Store Connect retains compliance status by version string, and an older string can re-trigger the prompt.
How to spot it: The compliance banner reappears even after you confirm the key is set in the latest build’s archived Info.plist. Build number is newer, but version string is the same or older.
6. Used qualifying encryption beyond HTTPS but did not file an annual self-classification
If your app uses non-exempt encryption that is not solely for the limited purposes Apple lists (authentication, copy protection, etc.), the US requires an Annual Self-Classification Report filed with BIS by Feb 1 each year. Skipping it does not block your build — Apple does not enforce — but you are technically in violation of US export law.
How to spot it: You marked ITSAppUsesNonExemptEncryption true, did not file an ERN, your app is not in any exempt category, and the calendar is past Feb 1.
Before you start
- Pull the actual
Info.plistfrom the archived.ipa, not just the source-controlled version — build settings sometimes overwrite or strip keys. - List every cryptography-related dependency in
Podfile.lock/Package.resolved. - Note which targets ship in the final app (App, Widgets, Watch, Intents, Share, NotificationService).
Information to collect
- Output of
grep -RE "ITSAppUsesNonExempt|ITSEncryption" .from the project root. - The list of crypto libraries (
grep -RE "OpenSSL|BoringSSL|libsodium|WireGuard|crypto" Podfile.lock Package.resolved 2>/dev/null). - A list of how the app uses cryptography (HTTPS only? CryptoKit? Custom AES? VPN?).
- App Store Connect → TestFlight → your build → the exact wording of the compliance warning.
- The previous build’s version string and whether it already had the key.
Step-by-step fix
Most apps land at Step 1 + Step 2 and never need anything beyond that.
Step 1: Audit your actual crypto usage
Three buckets:
- Exempt — uses only standard iOS encryption (HTTPS, Keychain, CryptoKit with stock algorithms): 95% of apps land here. Set
ITSAppUsesNonExemptEncryptiontofalse. - Exempt — uses non-standard crypto but qualifies for an exemption (only for authentication, copy protection, only crypto from Apple, etc.): Set the key to
trueAND declare the exemption viaITSEncryptionExportComplianceCodeafter one-time French / EU classification. - Non-exempt: Set the key to
true, file an annual self-classification with US BIS, and provide the report number.
Step 2: Add the key to every target’s Info.plist
For the App target:
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
If you use the modern Xcode build settings approach (no Info.plist file, keys in build settings), add under target → Build Settings → Info.plist Values:
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO
Repeat for every Extension target. The extension does not need to ship the encryption disclosure separately when the parent app declares it, but having the key present in each Info.plist avoids edge cases.
Step 3: If you must answer true, add the exemption code
After you submit the questionnaire in App Store Connect → TestFlight → your build → Provide Export Compliance, you receive an ITSEncryptionExportComplianceCode. Add it to Info.plist so future builds skip the prompt:
<key>ITSAppUsesNonExemptEncryption</key>
<true/>
<key>ITSEncryptionExportComplianceCode</key>
<string>YOUR-CODE-FROM-APP-STORE-CONNECT</string>
The code is per-app, not per-build, and persists across versions until your encryption usage materially changes.
Step 4: Rebuild, re-archive, and re-upload
A change to Info.plist needs a new archive — you cannot edit it in App Store Connect. Bump the build number:
agvtool next-version -all
Archive in Xcode (Product → Archive), validate, and upload. After processing, the yellow compliance banner should not appear.
Step 5: Verify the key actually made it into the binary
Download the processed .ipa from App Store Connect → TestFlight → Build → Download dSYM (or extract from your archive), then:
unzip -p YourApp.ipa "Payload/YourApp.app/Info.plist" \
| plutil -convert xml1 -o - - \
| grep -A1 ITSAppUsesNonExemptEncryption
You should see your declared value. If the key is missing, your build settings stripped it — check Skip Install, Strip PNG Text, and any custom build phase that processes Info.plist.
Step 6: If applicable, file the annual self-classification with BIS
For non-exempt encryption, email an Annual Self-Classification Report to crypt-supp8@bis.doc.gov and enc@nsa.gov by February 1 of each year. The report is a CSV with columns: product name, model number, ECCN (typically 5D002), description, item-specific authority, encryption details. There is no fee; the filing is purely informational and protects you from BIS audit.
Verify
- New TestFlight build shows Ready to Submit for testing without the yellow compliance banner.
- External testers see the build immediately after processing without manual reviewer action.
grep ITSAppUsesNonExemptEncryptionin the extracted binary’sInfo.plistreturns your chosen value.- App Store Connect → TestFlight → Build → Export Compliance section shows the answer locked in.
Long-term prevention
- Add the key to your Xcode project template so every new app starts with it set.
- Lock the value in source control — never edit it directly in Xcode without committing the change.
- When you add a new SDK, ask “does this ship custom encryption?” before merging; add to a project-level dependency review checklist.
- If your app graduates from exempt to non-exempt (e.g. you add a VPN feature), update the Info.plist key, get a new compliance code, and file the annual report. See TestFlight Build Stuck Processing when builds still take time to clear.
- Keep a one-page “Encryption Inventory” doc next to your
README.mdnoting which crypto your binary uses and the legal classification — it is the single most useful artifact during M&A or security review.
Common pitfalls
- Editing the Info.plist by hand in Finder while Xcode has the project open — Xcode rewrites it on next build, blowing away your edit.
- Setting the key to a string
"YES"/"NO"instead of a Boolean<true/>/<false/>. The string form is ignored by App Store Connect. - Assuming CryptoKit counts as non-standard crypto. CryptoKit uses Apple-provided algorithms — it is firmly in the “exempt” lane.
- Forgetting that adding
OpenSSLorlibsodiumvia a transitive Pod (gRPC, Firebase Auth bundle) changes your answer from exempt to potentially non-exempt. - Marking the build true once to “get past” the banner without filing the annual report — Apple does not enforce, but BIS does, and the penalty is non-trivial.
- Re-using a compliance code from a different app — codes are per-app and per-team.
FAQ
Q: My app only uses HTTPS. What is the right answer?
Set ITSAppUsesNonExemptEncryption to false. HTTPS / URLSession / Keychain / CryptoKit all qualify under the exemption that covers “encryption that is only used to support standard internet communication.”
Q: Can I just keep clicking through the App Store Connect questionnaire every build?
You can, but every external tester invitation pauses until you do. Setting the Info.plist key skips the prompt forever for that version string.
Q: I bundled a chat SDK that wraps libsodium. Does that make my app non-exempt?
Possibly. If libsodium is used to encrypt user messages end-to-end (beyond standard transport), and that is your app’s core function, you are non-exempt. If the SDK uses libsodium internally only for signing or key derivation incidental to its API, you likely still qualify for an exemption — but the safest path is true + exemption code from the questionnaire.
Q: My build was already approved with the key set to false. Is my older true answer haunted?
App Store Connect tracks compliance per version string. Bumping the version with the corrected key is enough; you do not need to remove older builds.
Q: Does the annual self-classification cost money?
No. The BIS filing is free; it just must arrive by Feb 1 each year for the previous calendar year’s products. The penalty for missing it is regulatory, not commercial — Apple will not pull your app.
Related
- TestFlight Build Stuck Processing
- TestFlight Build Expired
- Expired Provisioning Profile Build
- New Build Not Appearing
Tags: #Troubleshooting #App Store #TestFlight #encryption #export-compliance