Why Shopify Rejects Apps: The Review Failures That Cost Weeks

In this guide
- How Shopify frames the rules
- Failure 1: an app-wide billing gate (2.1.1)
- Failure 2: billing redirects that lose the session (2.3, 1.2.2)
- Failure 3: billing method mismatch (1.2.2)
- Failure 4: mock or fixture data in a real session (2.1.1, 1.1.4)
- Failure 5: SDK or runtime failures that silently fall back (2.1.1)
- Failure 6: the blank iframe (2.1.1, embedded checks)
- Failure 7: process crashes from a login route
- Failure 8: data you cannot read
- A pre-submit test that catches most of this
- Triage table: symptom to likely cause
- What to do after a rejection
Most Shopify App Store rejections are not judgment calls. They are one of a handful of defects that stop a reviewer from reaching a working feature, and the same few keep recurring. This guide lists the ones we hit repeatedly, which requirement each maps to, and how to test for it before you click submit. Disclosure: we build Shopify apps under Orbitloft; see /disclosure/. Everything below about our own submissions comes from our internal notes.
The short version: log in as a brand-new, unsubscribed merchant on a fresh store, with every sample-data switch off, and try to use your core feature. If you hit a redirect, an error page, a blank screen or placeholder data, a reviewer will too.
How Shopify frames the rules
Shopify publishes a requirements checklist and a detailed App Store requirements page with numbered rules. The ones that matter for the failures below:
| Requirement | What it says (paraphrased) | Typical failure |
|---|---|---|
| 1.1.4 | App and listing content must be factual; no falsified data | Fixture or sample data shown as if real |
| 1.2.1 to 1.2.3 | Use Shopify App Pricing or the Billing API; charges can be accepted or declined; plan changes without support or reinstall | Plan selection errors, dead-end after approval |
| 2.1.1 / 2.1.2 | No UI bugs, display problems or error pages (404/500) that block review | Redirect loops, Application Error screens |
| 2.3.x | Install starts on a Shopify surface, OAuth right after install and on reinstall | Bounce to a shop-domain form |
| 4.5 | Complete setup instructions, working credentials, English screencast | Reviewer cannot follow the test path |
Reviewers cite these by number in their feedback, so mapping a rejection to an ID tells you where to look.
Failure 1: an app-wide billing gate (2.1.1)
The most expensive pattern we found was a shared scaffold whose root /app loader checked for an active plan and redirected to the Plans page on every route when none existed. An unsubscribed reviewer could never leave the Plans page, so the rejection read “main functionality fails to appear”. It looks like a sensible paywall; to a reviewer it is a dead end.
Fix: the root loader should only authenticate and return what the shell needs. Keep the Plans page in navigation, and gate specific premium actions inside their own routes, never the whole app. Reviewers install with no plan, and your app must still demonstrate its core value.
Failure 2: billing redirects that lose the session (2.3, 1.2.2)
Two variants cost us several weeks.
- Bare redirects in loaders. Using a framework-level redirect instead of the one returned by the Shopify auth helper drops the
host,shopandid_tokenparameters. The first landing then bounces to the login form that asks for a shop domain. The fix is to use the redirect helper from the authenticate call, or manually keep the query string on any internal billing redirect. - A return URL built from the request. After client-side navigation the request URL had no query string, so the post-approval return URL degraded to a bare path and the merchant landed on a non-embedded page or an error. Appending the full query string was also wrong in our testing: the embedded query string carries a long session token and Shopify refused the return URL for being too long (we saw a 255-character limit; the API reference does not state one, so verify it yourself).
What worked: build the return URL from the session, as an admin deep link that re-embeds the app (https://admin.shopify.com/store/<store>/apps/<api-key>/app/billing), so it is short and always lands inside the admin. Also default test charges on a dev store to a flag you control and make sure it is off at launch, or real merchants are never charged.
Failure 3: billing method mismatch (1.2.2)
If you set pricing in the Partner Dashboard (Shopify App Pricing, formerly managed pricing), the Billing API is the wrong tool and the plan button errors out for every reviewer. We cover this in detail in Shopify Managed Pricing vs the Billing API. Check the Plans page loader as well as the button: a Billing API status check in the loader can crash the page before it renders.
Failure 4: mock or fixture data in a real session (2.1.1, 1.1.4)
This one got an app suspended in our case. The reviewer said the app did not show their products. The cause: data source selection depended on an environment variable that was stuck on true in the deployed environment, so a fully authenticated reviewer got the demo catalog. A second cause was a data function that swallowed API errors into an empty list, so failures looked like “no data”.
| Pattern | Why it fails review | Safer pattern |
|---|---|---|
USE_MOCK_DATA=true left on the host | Real store sees fixtures | Choose mock only when there is no authenticated admin client |
Errors caught and returned as [] | Reads as “no products” | Surface an error banner; reserve empty for a truly empty store |
| Hard-coded example prompts or seeded SKUs | Match nothing on a reviewer’s store | Derive examples from the live catalog |
| Verifying with the sample-data toggle on | Hides broken real API calls | Test with sample data off, on a store with real content |
The last row bit us after an app passed our own check: a renamed GraphQL enum made every real call fail, and we had only tested the sample path.
Failure 5: SDK or runtime failures that silently fall back (2.1.1)
One AI SDK failed in our hosting runtime with a premature-close stream error and dropped every feature into its mock fallback, so reviewers reported “only sample data”. Calling the HTTP API directly from the same runtime worked. The general lesson: a fallback hides failures from you as well as from the reviewer. Check production logs for a live, successful call, not just a rendered page.
Failure 6: the blank iframe (2.1.1, embedded checks)
A dashboard loader that waits on a slow external call, combined with a server-rendered body that renders nothing before hydration, shows a white screen for tens of seconds. It also looks like an authentication failure in the automated checks. Bound the slow call with a timeout and cache results; render a spinner on the server. Avoid AbortSignal.timeout on fetch in at least one Remix runtime we used, where it crashed the process; a Promise.race that stops waiting while the request finishes in the background was safe. More on the checks: Shopify Embedded App Check Explained.
Failure 7: process crashes from a login route
An unawaited login call in a route loader threw its redirect as an unhandled rejection and killed the Node process, so one visit to the login path restarted the instance for everyone. Await the call and catch thrown responses. Reviewers do probe odd URLs.
Failure 8: data you cannot read
Apps that query orders or customers return errors until protected customer data access is approved in the Partner Dashboard, which is separate from scopes. See Shopify Protected Customer Data: When You Need Approval.
A pre-submit test that catches most of this
- Install on a clean development store from your real production URL, not local.
- Do not pick a plan. Walk every nav item.
- Run the core feature with sample data off.
- Pick a plan, approve it, confirm you return inside the admin; decline once and confirm that works too (1.2.2).
- Uninstall and reinstall; confirm OAuth runs again (2.3.4).
- Open the browser console and server logs for errors.
- Complete the testing instructions, credentials and screencast, because the listing editor can lock while a submission is under review. Our listing fields were disabled in that state, though Shopify’s docs do not spell it out, so finish the listing first. Use the listing checklist.
Also watch for generic emails. Feedback arrived under a generic subject with the app named only in the footer, and went to whichever address the listing used. For timing expectations see what to expect from Shopify app review.
Triage table: symptom to likely cause
When a reviewer’s note is vague, match the symptom first. This table summarizes the patterns in the sections above.
| What the reviewer says | Likely cause | First thing to check |
|---|---|---|
| “Redirects to the subscription page” | App-wide billing gate | The root /app loader |
| “Error when selecting a plan” | Billing API call on a Shopify App Pricing app | Plan button action and Plans loader |
| “Redirected to an error page after approving the charge” | Return URL missing shop and host, or too long | How the return URL is built |
| “Only sample data” or “can’t see our products” | Mock gating or silent fallback | Data-source selection and server logs |
| “Blank screen” or “app doesn’t load” | Slow loader, empty server render | Time to first paint in the admin |
| “404 on plan selection” | Wrong app handle | Management URL in the store admin |
One more habit that paid off: keep a short written record of every reviewer note, the date, the requirement ID, and the commit that fixed it. After a few rounds the same IDs repeat, and the record becomes your pre-submit list.
What to do after a rejection
Compare the cited requirement ID with the list above before changing code. Fix the root cause across every app that shares the scaffold, not only the rejected one; our biggest time sink was fixing apps one rejection at a time. Then record proof of the fix. In our experience the feedback page wanted a hosted URL for the proof, so host a short screen recording somewhere public.
Frequently asked questions
What is the most common reason Shopify rejects an app?
In our own submissions it was requirement 2.1.1, the 'critical errors' rule: the reviewer could not reach or exercise a core feature. The usual causes were a billing gate that redirected every route, mock data shown to a real store, or an error page after a plan change. Shopify's checklist says error pages that block review completion are not acceptable.
Which requirement covers billing failures?
Requirement 1.2 (1.2.1 to 1.2.3 on the checklist). It says to use Shopify App Pricing or the Billing API, make charges acceptable and declinable, and let merchants change plans without contacting support or reinstalling.
Can I resubmit right after fixing a rejection?
Usually, but one of our suspended apps showed a date before which resubmission was blocked, so read the rejection email for any waiting period. In our experience the feedback page also asked for a hosted proof-of-resolution link before it would accept a fix.
Why does my app look broken in the admin when the server logs are fine?
A slow loader on the main app route plus an empty server-rendered body shows a white iframe until JavaScript finishes. Bound slow calls with a timeout, cache them, and render a spinner server-side so a reviewer sees progress.
Does Shopify tell you which app a rejection email is about?
Not clearly. In our experience the subject line was generic and the app name appeared only in the footer. Also check both the organization email and the listing's app submission email, which can differ.
Keep planning
- How to Choose a Shopify Agency Without Getting Burned
A decision guide to hiring a Shopify agency: scope first, fixed vs hourly, questions to ask, red flags, and why every agency list (including ours) is biased.
Updated Oct 2026 - Shopify App Store Listing Checklist: Assets, Copy, and Pricing
Every field in a Shopify App Store listing with its limits: icon, 1600x900 images, copy lengths, pricing rules, locked editor and visibility, as of October 2026.
Updated Oct 2026 - Shopify App Review Timeline: What to Expect After You Submit
What happens after you submit a Shopify app for review: the checks before, how long it can take, what statuses mean, and what to do after a rejection or pause.
Updated Oct 2026


