Fix ITSAppUsesNonExemptEncryption Missing Export Compliance

TestFlight asks for export compliance on every build because Info.plist does not declare ITSAppUsesNonExemptEncryption. Set it correctly and the prompt disappears.

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.plist from 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 ITSAppUsesNonExemptEncryption to false.
  • Exempt — uses non-standard crypto but qualifies for an exemption (only for authentication, copy protection, only crypto from Apple, etc.): Set the key to true AND declare the exemption via ITSEncryptionExportComplianceCode after 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 ITSAppUsesNonExemptEncryption in the extracted binary’s Info.plist returns 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.md noting 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 OpenSSL or libsodium via 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.

Tags: #Troubleshooting #App Store #TestFlight #encryption #export-compliance