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 greets you with a yellow banner: “Missing Compliance” with the prompt “Provide export compliance information.” Until you answer the questionnaire, the build sits with that warning and external testers cannot install 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 and asks again on every upload.

Fastest fix (95% of apps): add ITSAppUsesNonExemptEncryption set to <false/> to your app target’s Info.plist, bump the build number, archive, and re-upload. The banner never returns for that app. You only need the harder paths below if your binary ships non-standard crypto (a bundled VPN, libsodium, OpenSSL, custom AES, DRM) — in which case the value is <true/> plus an exemption code, and possibly a US self-classification (ERN) and a French declaration. As of June 2026 the Info.plist key, the App Store Connect flow, and the BIS Feb 1 filing deadline all still apply.

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 treats your answer as unprovided and prompts on every upload. New projects from older Xcode templates do not include it, and the modern SwiftUI App template does not add it either — you have to add it yourself.

How to spot it: grep ITSAppUsesNonExemptEncryption Info.plist returns nothing. The TestFlight build shows the yellow Missing 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. Find yours, then go to the matching step.

BucketWhat your binary usesITSAppUsesNonExemptEncryptionExtra paperwork
A (most apps)Only encryption built into iOS: HTTPS via URLSession, Keychain, CryptoKit with stock algorithms<false/>None
BNon-standard / proprietary crypto that still qualifies for a US exemption (used only for authentication, copy protection, or in a limited way)<true/> + ITSEncryptionExportComplianceCodeFrench declaration if you ship in France
CGenuinely non-exempt crypto (custom AES, bundled VPN, E2E messaging as your core feature)<true/> + ITSEncryptionExportComplianceCodeUS annual self-classification (ERN) to BIS + French declaration if you ship in France

Two important clarifications most guides get wrong:

  • The US exemption code (ITSEncryptionExportComplianceCode) and the French encryption declaration are different documents. The code comes from Apple after you answer the questionnaire; the French form is only required if you distribute on the App Store in France — and per Apple’s encryption documentation reference, it is needed for any industry-standard algorithm that is not provided by the Apple OS, not only for proprietary crypto.
  • Encryption that comes entirely from the Apple operating system needs no documentation in App Store Connect at all — that is exactly the <false/> path.

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

In App Store Connect, open Apps → your app → TestFlight, pick the platform in the sidebar, then click the build with the warning. Click Manage next to the build, click Provide Export Compliance Information, and answer the questions. If documentation is required, App Store Connect shows Go to App Encryption Page where you upload your file (or use Choose File to attach previously approved documentation). Once accepted, 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 genuinely non-exempt encryption (Bucket C), email an Annual Self-Classification Report to both crypt-supp8@bis.doc.gov and enc@nsa.gov by February 1 for the previous calendar year’s products. Per the BIS annual self-classification page, the report must be a CSV file (CSV is the only accepted format) whose first line is the header row with exactly these 12 fields, and no field may be left blank:

PRODUCT NAME, MODEL NUMBER, MANUFACTURER, ECCN, AUTHORIZATION TYPE, ITEM TYPE, SUBMITTER NAME, TELEPHONE NUMBER, E-MAIL ADDRESS, MAILING ADDRESS, NON-U.S. COMPONENTS, NON-U.S. MANUFACTURING LOCATIONS

For a typical mass-market mobile app the ECCN is usually 5D002 and the authorization type is the ENC mass-market exception, but you are responsible for classifying your own product. There is no fee. Two shortcuts worth knowing: if you made no exports of applicable items during the year, no report is required at all; and if nothing changed since your last report, you may simply email that nothing has changed (or resubmit the prior report) instead of rebuilding it.

How to confirm it’s fixed

  • The new TestFlight build no longer shows the yellow Missing Compliance warning after processing finishes.
  • External testers can install the build immediately after processing, with no manual action from you.
  • grep ITSAppUsesNonExemptEncryption in the extracted binary’s Info.plist (Step 5) returns your chosen value, not just the source file.
  • In App Store Connect → TestFlight → your build, the compliance status reads as provided rather than asking you to Manage it.

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 are all encryption provided by the operating system, which Apple’s ITSAppUsesNonExemptEncryption documentation treats as exempt — no documentation upload is required.

Q: Do I need the French encryption declaration?

Only if you distribute on the App Store in France. It is separate from the US exemption code. Per Apple, it is required even for industry-standard algorithms that are not provided by the Apple OS (and US CCATS plus the French declaration for proprietary algorithms). If you are in Bucket A — OS-provided encryption only — you need none of this.

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