How to Fix Apple's Privacy Manifest Error (ITMS-91053) in Your Expo App

DavidDavid August 8, 2026 8 min read
A laptop and a smartphone on a dark desk with moody blue accent lighting, an out-of-focus code editor glowing in the background
Original image, App Store Launch Club

What ITMS-91053 actually means

ITMS-91053, "Missing API declaration," means your app binary calls at least one API from Apple's Required Reason API list, and your privacy manifest does not declare an approved reason for it. It shows up two ways: as a warning email after you upload a build to App Store Connect, or, less often now, as an outright rejection at review. Either way it names the API category, not the line of code, which is why it feels harder to fix than it is.

This is not a bug in your app. Apple added this requirement so every app has to account for a short list of APIs that are commonly abused for fingerprinting, even when 95% of the apps calling them are doing something completely ordinary, like caching a file or reading a saved setting. Expo apps hit it constantly because the React Native runtime and its common dependencies use exactly those ordinary APIs.

The five Required Reason API categories

Apple's Required Reason list covers five categories. Any dependency that touches one of these needs a declared, approved reason code or your build gets flagged.

CategoryWhat commonly triggers it in an Expo app
UserDefaults accessAsyncStorage and most local key-value storage libraries
File timestamp APIsImage caching, file download, and asset libraries
System boot time APIsSome networking and analytics SDKs use it for retry/backoff timing
Disk space APIsLibraries that check available storage before a download or cache write
Active keyboard APIsCustom keyboard or input-related native modules

Where the declaration lives in a managed Expo project

You do not hand-edit a native PrivacyInfo.xcprivacy file in a managed Expo project. The declaration lives in app.json (or app.config.js) under expo.ios.privacyManifests.NSPrivacyAccessedAPITypes, as an array of entries pairing an API type with the reason codes that apply to your use of it.

  • expo.ios.privacyManifests.NSPrivacyAccessedAPITypes takes an array of objects
  • Each object sets NSPrivacyAccessedAPIType to a category constant, e.g. NSPrivacyAccessedAPICategoryUserDefaults
  • Each object also sets NSPrivacyAccessedAPITypeReasons to the array of reason codes that apply

When your ios/ folder is not committed, which is the default for a managed project, EAS Build runs expo prebuild before every native build. Prebuild regenerates the Xcode project from app.json fresh, including writing this config into the resulting PrivacyInfo.xcprivacy file. That is why the fix is a config.json edit, not a native code change, and why the declaration survives every rebuild instead of getting wiped.

Finding which dependency is actually the culprit

The App Store Connect warning names an API category, not a package. Here is the order I check, fastest first.

  1. Search node_modules for PrivacyInfo.xcprivacy files: most well-maintained packages that touch a Required Reason API already ship their own manifest inside their ios/ folder.
  2. If a dependency ships one, copy its declared NSPrivacyAccessedAPITypes entries into your own app.json privacyManifests block. Apple does not reliably parse every manifest bundled inside a third-party static CocoaPods dependency, so even a package that did the right thing can still get flagged at your app's top level.
  3. If no dependency manifest exists, upload the build to TestFlight anyway and wait a few minutes. Apple's processor often emails you directly naming the exact missing API category, which is faster than guessing from the App Store Connect warning alone.
  4. Cross-reference that category against what you actually added recently: a new analytics SDK, a new storage library, a new image-caching package are the usual suspects.

The declare, verify, resubmit loop

I run the same three-step loop on every Expo app before it goes anywhere near App Store Connect, because catching this pre-submission is much cheaper than catching it at review.

  1. Declare: after adding any new native dependency, check its ios folder for a shipped PrivacyInfo.xcprivacy and merge its reasons into app.json before your next build.
  2. Verify: run a TestFlight internal build after any dependency change, not just before the final submission. The processing email surfaces missing declarations in minutes.
  3. Resubmit: fix the app.json entry, rebuild, and re-upload. Because prebuild regenerates the manifest from config every time, you never have to touch native files by hand.

A mistake worth avoiding

The mistake I see most is treating ITMS-91053 as a one-time fix. You clear it, ship, and move on, then six weeks later you add a new SDK for push notifications or in-app purchases and the warning is back, because that new dependency touches a different Required Reason API. Treat the declare-verify-resubmit loop as a permanent part of adding any native dependency, not a checkbox you tick once before your first launch.

Free app-building tips, straight to your inbox

Short, practical drops on app ideas, validating fast, store listings, and passing app review. No spam, unsubscribe anytime.

Frequently asked questions

What does ITMS-91053 mean for an Expo app?

It means a dependency in your app calls a Required Reason API (UserDefaults, file timestamp, system boot time, disk space, or active keyboard) and your privacy manifest has no declared reason for it. Apple flags the category, not the exact line of code, so you have to trace which dependency is responsible.

Where do I add the privacy manifest declaration in a managed Expo project?

In app.json (or app.config.js) under expo.ios.privacyManifests.NSPrivacyAccessedAPITypes. You do not hand-edit a native PrivacyInfo.xcprivacy file - EAS Build's prebuild step regenerates the native project from this config on every build.

Does Expo Go trigger this error?

No. Expo Go is a shared client that never gets submitted to the App Store, so it is never checked against Required Reason API declarations. The warning only shows up on a real EAS Build binary once you upload it to App Store Connect or TestFlight.

Why does AsyncStorage trigger a Required Reason API warning?

AsyncStorage and most local key-value storage libraries read and write through UserDefaults under the hood, which is one of Apple's five Required Reason API categories. Almost every React Native app uses some form of local storage, which is why this specific category is the most common trigger.

How do I find which dependency is causing the warning?

Search node_modules for PrivacyInfo.xcprivacy files first - many packages already ship one you can copy the reasons from. If nothing turns up, upload a TestFlight build; Apple's processing email often names the exact API category within minutes, which narrows it down faster than guessing.

Can I ignore ITMS-91053 if it is only a warning, not a rejection?

You can ship past a warning in the short term, but it does not go away and can escalate to a hold at review once Apple tightens enforcement on your category. Treat it as a required fix, not an optional cleanup, and declare the reason properly the first time.

Last reviewed by David on August 8, 2026

David

Written by

David

Founder and app builder

Keep reading

App StoreLaunch

App Store Phased Release: How the 7-Day Rollout Works, How to Pause It, and When to Skip It

App Store phased release rolls a version update out to a random sample of users with automatic updates on over 7 days: 1 percent on day one, then 2, 5, 10, 20, 50 and 100. You can pause it for up to 30 days in total, or release to everyone with one button. Here is exactly what Apple says it does, what it does not do, and how I decide when an Expo app update should use it.

David 8 min
Read article
Apple App ReviewApp Store

App Store Age Rating Questionnaire: How to Answer It Correctly (4+, 9+, 13+, 16+, 18+)

The App Store age rating questionnaire is the required set of questions in App Store Connect that decides whether your app shows as 4+, 9+, 13+, 16+ or 18+. Apple calculates the rating from your answers, and a handful of capability questions (unrestricted web access, social media, medical information) push a plain utility app up two tiers if you answer them loosely. Here is what each question means, which answers move the rating, and how to fill it in for an Expo app.

David 9 min
Read article

Ready to build it yourself?

Join App Store Launch Club, the #1 community for building and launching apps, for $9/month.

← Back to the blog