shipcheck

ITMS-91054, ITMS-91055 and ITMS-91056

Updated September 6, 2026 · published by Baker Ventures LLC · sources cited inline

These three are the "you declared something, but wrong" errors, as distinct from ITMS-91053 which is "you declared nothing" and ITMS-91061 which is "this framework has no manifest at all."

CodeMeansUsual cause
ITMS-91054Invalid API category declarationA display label was used where a key string was required
ITMS-91055Invalid API reason declarationA reason code that is not valid for that category
ITMS-91056Invalid privacy manifestThe file will not parse — malformed XML or wrong structure

All three name a file path, which is the useful part and the part people skim past. Unlike 91053, you are not hunting through 800 packages — the message tells you which file to open.

ITMS-91054 — invalid API category declaration

The error quotes the offending value. A representative message reads that the PrivacyInfo.xcprivacy for a given framework "contains 'Disk Space' as the value for a NSPrivacyAccessedAPIType key, which is invalid."

That is the entire bug: Disk Space is the label Xcode displays; the value Apple's tooling requires is the key string. Xcode's property-list editor renders friendly names for known keys, and if you type or paste what you can see rather than the raw key, you produce a file that looks correct in the editor and fails at upload.

There are exactly five valid values:

NSPrivacyAccessedAPICategoryFileTimestamp
NSPrivacyAccessedAPICategorySystemBootTime
NSPrivacyAccessedAPICategoryDiskSpace
NSPrivacyAccessedAPICategoryActiveKeyboards
NSPrivacyAccessedAPICategoryUserDefaults

Nothing else is accepted. Not UserDefaults, not User Defaults, not NSUserDefaults, not NSPrivacyAccessedAPICategoryUserDefault singular.

To see what is really in the file, read it as raw XML rather than in Xcode's editor:

plutil -convert xml1 -o - path/to/PrivacyInfo.xcprivacy

ITMS-91055 — invalid API reason declaration

The category is valid; the reason code inside NSPrivacyAccessedAPITypeReasons is not one Apple accepts for that category. Reason codes are category-specific and are not interchangeable — CA92.1 is a UserDefaults reason and means nothing under DiskSpace.

The three ways this happens:

A code copied from the wrong category. Usually from a blog post covering a different one.

A code that has drifted. Apple states it "continually reviews the list of required reason APIs and reasons for usage, and will update this article from time to time." A code that was valid when a tutorial was written may not be now.

A code from an incorrect secondary source. Apple's documentation renders client-side, so a plain HTTP fetch of it returns an empty page. That is precisely why wrong copies of the reason-code table circulate — the people compiling them could not read the original either.

We transcribed the complete list in a real browser on 6 September 2026, with dates, for this reason: every category and every valid reason code.

ITMS-91056 — invalid privacy manifest

The file will not parse or is structurally wrong. The message names a path.

Most common causes, in order:

  1. Malformed XML. An unclosed tag, a stray character, a truncated file from a bad merge.
  2. Wrong root type. The root must be a dictionary.
  3. Keys at the wrong nesting level. NSPrivacyAccessedAPITypeReasons must sit inside each dictionary in the NSPrivacyAccessedAPITypes array, not beside the array.
  4. Wrong value types. NSPrivacyAccessedAPITypes is an array of dictionaries; NSPrivacyAccessedAPITypeReasons is an array of strings. A bare string where an array is expected fails.
  5. A merge conflict marker left in the file. It happens more often than anyone admits.

Validate before you upload — this costs one second:

plutil -lint ios/YourApp/PrivacyInfo.xcprivacy
find . -name "PrivacyInfo.xcprivacy" -not -path "*/node_modules/.cache/*" -exec plutil -lint {} \;

A structurally correct manifest

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>NSPrivacyTracking</key>
  <false/>
  <key>NSPrivacyCollectedDataTypes</key>
  <array/>
  <key>NSPrivacyTrackingDomains</key>
  <array/>
  <key>NSPrivacyAccessedAPITypes</key>
  <array>
    <dict>
      <key>NSPrivacyAccessedAPIType</key>
      <string>NSPrivacyAccessedAPICategoryUserDefaults</string>
      <key>NSPrivacyAccessedAPITypeReasons</key>
      <array>
        <string>CA92.1</string>
      </array>
    </dict>
    <dict>
      <key>NSPrivacyAccessedAPIType</key>
      <string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
      <key>NSPrivacyAccessedAPITypeReasons</key>
      <array>
        <string>C617.1</string>
      </array>
    </dict>
  </array>
</dict>
</plist>

Those two entries — CA92.1 for UserDefaults and C617.1 for file timestamps in your own container — are the normal, accurate declarations for an ordinary React Native app. They are not a template to paste without checking: declare only what your app actually does, because Apple's terms bind you to the declared reason and several codes carry conditions such as not sending the data off-device.

The Expo trap that makes this recur

With continuous native generation, ios/ is regenerated from app config and config plugins on every prebuild. Fix the manifest in ios/YourApp/PrivacyInfo.xcprivacy, verify locally, push, and EAS overwrites it — same error, second cycle, and the change was correct both times.

Express it in app.json / app.config.js or a config plugin so it survives. Why RN and Expo fail differently.

Catching all three before you build

Every one of these is a static, local, deterministic check: lint every manifest in the tree, validate every category string against the five valid keys, and validate every reason code against the set Apple accepts for its category. None of it needs a build, an upload or a review queue.

That is part of what shipcheck runs, inside the Claude Code session you already have open, against local files. What it checks.

Questions and answers

What is ITMS-91054?

Invalid API category declaration. A PrivacyInfo.xcprivacy file contains a value for NSPrivacyAccessedAPIType that is not one of Apple's five valid category strings. The email names the file and quotes the invalid value - for example that a file "contains Disk Space as the value for a NSPrivacyAccessedAPIType key, which is invalid".

What is ITMS-91055?

Invalid API reason declaration. The category string is valid but the reason code inside NSPrivacyAccessedAPITypeReasons is not one Apple accepts for that category. Reason codes are category-specific and are not interchangeable.

What is ITMS-91056?

Invalid privacy manifest. The PrivacyInfo.xcprivacy file at a given path could not be parsed or is structurally wrong - malformed XML, wrong root type, or keys at the wrong level. The message names the path, which is the most useful part of it.

What is the difference between ITMS-91053 and ITMS-91054?

ITMS-91053 means a required reason API is used and nothing is declared for it. ITMS-91054 means something is declared but the category string itself is invalid. The first is an omission, the second is a typo or a human-readable label used where a key was expected.

Why does my privacy manifest say Disk Space is invalid?

Because the value must be the key string NSPrivacyAccessedAPICategoryDiskSpace, not the display name Xcode shows you. Xcode's plist editor renders friendly labels, and if you copy what you see rather than the raw key, App Store Connect rejects it.

Cite this pageshipcheck. “ITMS-91054, ITMS-91055 and ITMS-91056.” Baker Ventures LLC, September 6, 2026. https://shipcheck.bakerventuresstudio.com/rejections/itms-91054-91055-91056-invalid-privacy-manifest/