When a dependency is blocking your release
First, find the actual package. Search for the string in the error, not for the feature you think it relates to.
# the symbol, SDK name or permission from the error message
grep -rl "STRING_FROM_THE_ERROR" node_modules ios/Pods 2>/dev/null | head
grep -n "STRING_FROM_THE_ERROR" package-lock.json yarn.lock 2>/dev/null | head
# which of your direct dependencies pulls it in
npm ls PACKAGE_NAME 2>/dev/null | head -20Then five options, ranked:
1. Update it. Usually the answer, because everyone using the library hit the same wall at the same time and maintained projects fix these fast.
2. Replace it. If the current version still fails, the library is unmaintained on this point, and a patch you carry forever is worse than a migration you do once.
3. Remove the feature. Genuinely on the list. A capability nobody uses that blocks every release is not worth what it costs.
4. Patch it locally. Unblocks today. Becomes permanent, silently. A patch survives installs and does not survive a major version bump, so it turns into a maintenance obligation nobody remembers accepting.
5. Fork it. Only when it is genuinely unmaintained, you need it, and nothing else exists. Forking means owning it, which is a decision about the next two years rather than about this release.
Ordering matters because options one to three end the problem, and four and five relocate it.
The four blockers this usually is
A missing privacy manifest. The SDK is on Apple's list and ships no PrivacyInfo.xcprivacy. Update first; most maintained libraries added one. ITMS-91061. · Which RN SDKs need one. · The SDK to npm map.
A required-reason API with no declared reason. Same shape, different error. ITMS-91053. · The reference.
A private API call. Static analysis flagged a symbol. Sometimes it is your own symbol colliding, which is a five-minute rename. Guideline 2.5.1.
A permission or purpose string you did not add. On iOS it arrives through a config plugin or a linked framework; on Android through manifest merging. ITMS-90683. · The Play permissions declaration.
Doing it in Expo, correctly
The trap is the same every time: ios/ and android/ are build artefacts. A fix made there works locally, disappears at the next prebuild, and produces an identical second failure with the change having been right both times.
Fixes belong in app.config.js or a config plugin. A plugin that adds a manifest entry, removes a permission, or writes a privacy manifest key is twenty lines and it survives.
# does the fix survive a regeneration
npx expo prebuild --clean
grep -n 'THE_THING_YOU_FIXED' ios/*/Info.plist android/app/src/main/AndroidManifest.xmlRun that before you believe a fix. Auditing what your config plugins added. · The EAS submission checklist.
If you patch
Do it deliberately, and leave a note that survives you:
patches/
react-native-thing+4.2.1.patch
README.md <- why this exists, what upstream issue it tracks,
and what has to be true before it can be removedThe README is the whole difference between a patch and a mystery. Without it, the next person finds a patch nobody understands against a version nobody remembers, and it is never removed.
Same for a git dependency. Installing from an unreleased commit is a legitimate short-term measure. Record why, and move to the released version when it lands.
Preventing the next one
Audit native dependencies on a schedule, not before a release:
# packages with native code, which is where this class of problem lives
for d in node_modules/*/ node_modules/@*/*/; do
ls "$d"*.podspec "$d"ios/*.podspec >/dev/null 2>&1 && basename "$d"
done | sort -u | head -40
npm outdated 2>/dev/null | head -30Keep the native dependency count deliberate. Every native package is a route for someone else's maintenance decision to become your blocked release, which is the same argument as not shipping a capability you do not use.
And check before the deadline. A privacy manifest gap found in an audit is an update. The same gap found on submission day is an emergency, and the emergency is the version where people patch.
What to do when your app is rejected. · What changes at each OS release.
shipcheck reports the SDKs in your tree that appear on Apple's list, which ship a privacy manifest and which do not, and which package pulls in each one, so the audit is a report rather than an afternoon. What it checks.
More in this section
About shipcheck
shipcheck is a pre-submission checker from Baker Ventures LLC that runs inside Claude Code. It reads your React Native or Expo project, finds the things that get apps rejected or blocked at upload, cites the exact App Store Review Guideline or Google Play policy clause, and names the file and line to fix.
It exists because React Native and Expo apps fail for reasons that are invisible in the code you wrote: capability arriving through a dependency, purpose strings added by a config plugin, a privacy manifest missing from an SDK you never chose directly, and a generated ios/ directory that discards your edits on the next prebuild. shipcheck checks what the binary and the config actually declare, not what you intended. The rejection references on this site are free, need no account, and link to the primary Apple or Google document for every claim.
Questions and answers
What do you do when a library is blocking your app release?
There are five realistic options: update it, replace it, remove the feature, patch it locally, or fork it. Ranked by time to unblock and by what each costs you afterwards, updating and replacing are usually right and patching is the one that quietly becomes permanent.
How do I find which dependency is causing the problem?
Search the compiled output and the lockfile for the specific symbol, SDK name or permission from the error, rather than searching for the feature you think it relates to. The error names something searchable and that string is the fastest route.
Is patch-package a real solution?
It is a real unblock and a poor long-term answer. A patch survives installs and does not survive a major version bump, so it becomes a maintenance obligation nobody remembers taking on. Use it to ship, then do the real fix.
When should you fork a dependency?
When it is genuinely unmaintained, you need it, and no alternative exists. Forking means owning it, so it is a decision about the next two years rather than about this release.
How do you avoid this happening again?
Audit native dependencies on a schedule rather than before a release, keep the count deliberate, and prefer maintained libraries for anything touching native code. Most of this class of problem is a maintenance question wearing a compliance costume.
What if the maintainer has already fixed it but not released?
Installing from a git commit is a legitimate short-term measure and it pins you to an unreleased state. Note why, and move to the released version when it lands, because an unexplained git dependency is the kind of thing nobody dares change later.
shipcheck. “When a dependency is blocking your release.” Baker Ventures LLC, September 7, 2026. https://shipcheck.bakerventuresstudio.com/guides/when-a-dependency-blocks-your-release/