Rejection is rarely caused by a lack of knowledge about the rules. It is caused by process gaps that let a known rule slip through. This document names the recurring mistake patterns, the human root cause behind each, and the discipline that prevents it. Then it gives the appeal playbook for when a rejection still lands.
The store reviewer is an adversarial integration test that runs once, on a real device, with no access to your intentions and no patience for setup. Every developer who is surprised by a rejection made the same error. They optimized for building the app and treated submission as a final formality, when submission is itself a test with its own preconditions. The fix is to move the review criteria to the front of the process and treat each one as a release blocker, not a last mile checklist.
Root cause. The reviewer could not reach a feature, so they rejected what they could not see. A login wall with no demo account, a backend that was paused to save cost during review, a feature behind a paywall the reviewer did not buy, a region locked flow.
The discipline. Provide a working demo account and a live backend at submission. Walk the Notes for Review field through every non obvious path. If the reviewer cannot reach it, it does not exist.
Root cause. What the app does at runtime does not match what was declared. Apple privacy labels or Google Data Safety say one thing while an SDK does another. A purpose string is generic. The listing promises a feature the build lacks.
The discipline. Audit actual runtime data flows, including every third party SDK, against every declaration. The declaration is a contract. Make the app honor it exactly.
Root cause. Trying to avoid the store commission on digital goods by routing payment outside the official billing. License keys, external checkout, crypto, or a web link for in app digital content.
The discipline. Use Apple in app purchase and Google Play Billing for all digital goods unless the app is a documented exempt category. Treat the commission as a cost of distribution, not a bug to route around.
Root cause. Shipping a website in a native shell with no added value. The app is a web view, a marketing page, or a link collection. Apple 4.2 and Google minimum functionality both reject this.
The discipline. Add genuine native capability, offline value, device integration, or content the web version does not have. If the app does nothing beyond the website, it is not ready.
Root cause. Submitting many near identical apps, duplicate bundle IDs for variations, template or generated apps from a reseller, or a low effort clone in a saturated category. Apple 4.3 and Google spam policy both catch this, and both escalate to account level action.
The discipline. One app per concept, with variations handled as in app content. If a content owner has many properties, the owner submits or uses a single picker app.
Root cause. Requesting sensitive permissions with no matching core feature. Background location, all files access, SMS or call log, AccessibilityService used for data collection. Google rejects the permission without a qualifying use case, Apple rejects the vague purpose string.
The discipline. Request the minimum. Every sensitive permission must trace to a visible core feature, carry a specific reason string, and use the platform declaration form where required.
Root cause. The app collects data or creates accounts but gives users no way out. No account deletion inside an app that supports account creation, no consent withdrawal, no privacy policy, social tokens stored off device.
The discipline. Every account creating app needs in app account deletion. Every data collection needs consent and a withdrawal path. The privacy policy must be present, accessible, and accurate.
Root cause. Treating a rejection as a coin flip and resubmitting the same build hoping for a different reviewer. On Google this is the fastest path from rejection to suspension to account termination.
The discipline. Never resubmit without addressing the cited guideline. Read the exact rule, fix the cause, and reply in the Resolution Center or the Play appeal with what changed.
A rejection is recoverable. A bad appeal turns one rejection into a strike.
Address the cited rule, show evidence, change the build if needed, and reply once with everything. Speed comes from precision, not from volume.
Named, real rejections teach more than an abstract rule. These come from open source community collections.
The lesson across all four. Approval is a moment, not a state. The rules apply at submission and keep applying after, so the privacy strings, the category fit, and the current SDK all have to stay correct over the life of the app.
Put the pre submission checklist into the definition of done for every release. Run the automated guard before every upload. Keep this taxonomy in code review so a reviewer can name the pattern a change risks. The goal is that no rejection reason in this document can reach submission without a human or a hook catching it first.