What the rejection typically looks like
Guideline 2.1(a) - Performance - App Completeness
Issue Description
The app exhibited one or more bugs that would negatively impact users.
Bug description: The app crashed after we tapped "Continue" on the onboarding screen.
Review device details:
- Device type: iPad Air 11-inch (M3)
- OS version: iPadOS [version]
- Internet connection: Active
Next Steps
Test the app on supported devices to identify and resolve bugs and stability issues before submitting for review. Crash logs for this issue are attached.
Paraphrased example – the exact wording in your message may differ.
What Guideline 2.1 App Completeness actually means
Guideline 2.1 opens the Performance section of the App Store Review Guidelines (the version live in October 2026 is marked "Last Updated: June 8, 2026") and has two parts:
- 2.1(a) – every submission, including apps offered for pre-order, must be a final version: complete metadata, fully functional URLs, no placeholder text or empty websites, tested on-device for bugs and stability, and demo account details plus a running back-end if the app has a login. Apple is blunt: "We will reject incomplete app bundles and binaries that crash or exhibit obvious technical problems."
- 2.1(b) – in-app purchases must be "complete, up-to-date, visible to the reviewer and functional". If a configured product can't be found or reviewed in the app, explain why in the review notes.
The same number is also used when App Review needs a demo account, a video or more details. That has a different fix, covered in our guide to Guideline 2.1 Information Needed. This page covers the technical case: the reviewer tried your app and something was broken or unfinished.
Read the Bug description and Review device details first. They tell you what the reviewer did, on which device and OS version, and whether the device had an internet connection. Many apps fail on an iPad although the team only tested on iPhones.
Common reasons apps get rejected under 2.1
| Trigger | What the reviewer experiences |
|---|---|
| Crash on launch or in a core flow | The app closes on the splash screen, after a permission prompt or on a button tap. Crash logs are usually attached. |
| Login or onboarding dead end | Credentials are accepted but the app returns to the login screen, Sign in with Apple fails, or buttons don't respond. |
| Endless spinner or error screen | The API is offline, points to staging, blocks the reviewer's region or rate-limits unknown IPs. |
| IPv6-only network failure | Content never loads because the app or an SDK uses IPv4-only socket code (AF_INET, sockaddr_in, inet_addr), or because the server publishes an IPv6 (AAAA) record it doesn't actually serve. |
| Placeholder content | Latin dummy text, sample images, "Coming soon" tabs, unexplained empty lists, test products. |
| Broken links | Support, privacy policy or terms links lead to 404 pages or parked domains. |
| In-app purchases not reviewable (2.1(b)) | The paywall shows no prices, the purchase sheet never appears, or configured products are nowhere in the app. |
| Beta or test leftovers | "Beta" badges, debug menus or environment switchers. Guideline 2.2: "Demos, betas, and trial versions of your app don't belong on the App Store" – use TestFlight. |
| iPad layout or crash | Your iPhone app runs on iPad in compatibility mode and breaks. See our iPad compatibility guide. |
Rejected or stuck? Talk to an App Specialist – for free.
Book a free consultation call: we look at your rejection or setup, explain the fastest way forward and tell you honestly whether you need us. Prefer to hand it off? Book our AI-powered + human-powered service and we take care of it.
How to read the crash log Apple attaches
When the reviewer records a crash, the rejection usually comes with crash reports attached. In App Store Connect, open your app, click App Review in the sidebar, open the rejected submission and check Messages. If no log is attached, ask for one in your reply. Then symbolicate it, which turns hexadecimal addresses into function names:
- Make sure the file ends in
.ipsor.crash; Apple says to rename it otherwise. - Open it in Xcode and choose your project when prompted. Xcode needs the dSYMs of that exact build; keeping the archive in Window > Organizer > Archives is the easiest way to have them on hand.
- Or run
xcrun crashlog /path/to/report.ipsin Terminal to resolve symbols and line numbers in LLDB. Apple documents both in Adding identifiable symbol names to a crash report. - If your own frames stay unsymbolicated, the dSYM is missing. Make your CI archive dSYMs for every build and upload them to your crash reporter.
What to look at first
| Field or signature | Typical meaning |
|---|---|
EXC_BREAKPOINT (SIGTRAP) | Swift runtime trap: force-unwrapped nil, index out of range, failed precondition or fatalError. |
EXC_CRASH (SIGABRT) | Uncaught Objective-C or C++ exception, or an explicit abort. Check the Last Exception Backtrace. |
EXC_BAD_ACCESS | Access to freed or invalid memory, often in Objective-C, C++ or a native SDK. |
Termination Reason namespace TCC | Privacy-sensitive data such as the camera, microphone, photo library or contacts accessed without the matching usage description in Info.plist. A missing location key doesn't crash; the permission prompt simply never appears. |
Termination Reason code 0x8badf00d | Watchdog kill: the main thread was blocked too long, often by synchronous network or database work at launch. |
Read the crashed thread top-down to the first frame from your own code. Together with the bug description, it usually reveals an environmental assumption: a missing config value, a denied permission, an empty API response or a slow network.
Other crash sources: the Crashes organizer in Xcode (Window > Organizer > Crashes) collects reports from TestFlight testers automatically, and the TestFlight section of App Store Connect shows tester feedback, including comments on crashes. Apple notes that watchdog terminations, memory (jetsam) events and invalid code-signature crashes don't appear in the Crashes organizer. Pull those from a test device under Settings > Privacy & Security > Analytics & Improvements > Analytics Data.
Flutter and React Native: an unhandled JavaScript exception in a React Native release build aborts the app, so the native report shows SIGABRT with the JS error in the Last Exception Backtrace. Uncaught Dart errors in Flutter usually don't crash at all. The reviewer sees a grey or blank screen and reports a bug without a crash log. Check your crash reporter (Crashlytics, Sentry) for the Dart or JS stack. Typical release-only causes: environment variables CI never injected, a missing GoogleService-Info.plist for the release configuration, or a JS bundle that wasn't embedded.
How to fix a 2.1 App Completeness rejection step by step
- Note the reviewer's conditions: device, OS version, internet connection, steps from the bug description, new install or update.
- Test the exact binary. Install the rejected build through TestFlight (internal testers don't need Beta App Review) and launch it from the home screen without Xcode attached. Apple's Testing a release build explains why: the Xcode debugger disables watchdog terminations, and release build settings can differ from debug.
- Match the device. If an iPad is named, test on an iPad in both orientations and in Split View. If you don't own the exact model, use the matching Simulator for layout checks, but confirm crashes on real hardware with the reported OS version.
- Recreate a first launch. For a new app, delete all previous versions. For an update, install the current App Store version, use it, then update over it to exercise data migrations and keychain items.
- Recreate the reviewer's choices. Deny every permission, skip optional onboarding, use the demo account from App Review Information, and test on a slow or IPv6-only network.
- Fix the root cause. Replace force unwraps with handled errors, move launch work off the main thread, add missing usage descriptions, and give network calls a timeout, an error state and a retry button.
- Remove everything unfinished. Search strings and assets for filler text, sample images, "beta", "test" and "coming soon". Hide incomplete features instead of shipping empty tabs.
- Check every link in the app plus the Support, Marketing and Privacy Policy URLs in App Store Connect.
- Ship a new build: increment
CFBundleVersion, archive, upload, retest in TestFlight, select it on the version page and describe the fix in the review notes.
Backend outages and IPv6-only networks during review
Many 2.1 rejections come from the server, not the app. The guidelines tell you to "turn on your back-end service", and a reviewer can't tell an offline API from a broken app.
Keep the backend reachable
- Point the release build at production, not a staging host that sleeps at night.
- Apple doesn't publish where reviewers are located, so assume they may connect from outside your target market. Check geo-blocking, country-restricted SMS verification, IP allowlists and bot protection.
- Avoid maintenance, certificate rotations and migrations while a submission is in review.
- Keep the demo account active, unlocked and filled with realistic data.
Work on IPv6-only networks
Guideline 2.5.5 requires apps to be fully functional on IPv6-only networks. Apple's Supporting IPv6 DNS64/NAT64 Networks guide even refers to "the one used during App Review" when it describes these networks. What to check:
- Connect by hostname with
URLSessionor the Network framework. These APIs handle DNS64/NAT64 for you; problems come from low-level socket code that uses IPv4-only types or IP address literals. - Audit SDKs, WebSocket clients, VoIP and video libraries for hard-coded IPs and IPv4-only sockets.
- Test locally with a NAT64 network shared from a Mac that is online over Ethernet or USB, not Wi-Fi: in System Settings > General > Sharing, Option-click the info button next to Internet Sharing, enable Create NAT64 Network and join that Wi-Fi with your device. The option is hidden and its location has moved between macOS versions.
- Verify the server separately. A Mac NAT64 network always translates to IPv4, but the network used in review allows direct IPv6 connections. If your server publishes an AAAA record (
dig AAAA api.example.com), it must really answer over IPv6, or it can pass your local test and still fail in review.
Guideline 2.1(b): in-app purchases the reviewer can't find or buy
A typical 2.1(b) message says the in-app purchases "do not load successfully", or that the reviewer could not find them in the app. Check:
- Submission: the first in-app purchase of each type and the first subscription must be submitted with a new app version. In App Store Connect go to Monetization > In-App Purchases or Subscriptions, click Add for Review and include the app version (Submit an In-App Purchase). A product still in "Prepare for Submission" was never sent to review.
- Agreement: the Account Holder has accepted the Paid Apps Agreement (Business > Agreements) and the banking and tax information is complete.
- Product IDs match exactly across your code, your purchase SDK and App Store Connect.
- Sandbox: App Review purchases run in the sandbox. If your server validates purchases, it must accept sandbox transactions from a production build: use the sandbox environment of the App Store Server API for transactions marked Sandbox, or, on the deprecated
verifyReceiptflow, retry against sandbox when production returns status21007. - Resilient paywall: timeout, automatic retry and a visible retry button. An empty product list shows a message, not an endless spinner.
- Hidden products: if a purchase only appears after onboarding or a usage threshold, describe the path in the review notes.
Pricing, unlocking and external payment questions fall under Guideline 3.1.1 In-App Purchase.
How to respond to App Review
- Code bug fixed: upload a new build, select it and resubmit. Summarize cause, fix and verification steps in the review notes and in a reply to the rejection.
- Server-side only (outage, expired certificate, blocked region): fix it, explain what happened in a reply and resubmit. The same build can often be reviewed again.
- Can't reproduce: reply to the message with the devices, OS versions and networks you tested, and ask for exact steps or a screen recording. You can attach files to your reply. App Review staff also point developers to the App Review tile on Apple's Contact Us page to request a call about the outcome.
- Reviewer misunderstood intended behavior: explain in a reply first. Use an appeal to the App Review Board only if you are sure the app complies and you have answered any open questions; Apple allows one appeal per rejected submission.
Keep replies short and factual. Instead of "it works on our devices", name the device, OS version, build number and test conditions.
How to prevent 2.1 rejections next time
Most 2.1 rejections are preventable with a repeatable release routine:
- CI smoke tests: XCUITests for launch, login, a core action and the paywall on iPhone and iPad simulators, plus a StoreKit configuration file for local purchase tests.
- Leftover scan: fail the build on filler text, "beta" strings, debug flags or staging URLs in the release configuration.
- Link check: crawl every URL in your strings and store metadata.
- dSYM archiving on every build, so any crash symbolicates immediately.
- TestFlight soak with testers outside the team, on older devices and an IPv6 network, while watching the Crashes organizer.
- Backend monitoring while a submission is in review.
AI-assisted checks take over the tedious parts: triaging crash logs, spotting placeholder text in screens and comparing review notes with what the build does. If you want a second pair of eyes before resubmitting, appsubmitter.io combines those checks with experienced release engineers. Book a free consultation call and walk us through your rejection.
Template: how to reply to App Review
Adapt this template to your situation. Keep it factual, short and specific – and only claim what you have actually changed.
Checklist before you resubmit
- Every attached crash log is symbolicated and its root cause identified.
- The bug is fixed and verified on the device type and OS version from the review device details, including iPad.
- A fresh install and, for updates, an install over the current App Store version both work.
- The release build launches from the home screen without a debugger and survives a slow network.
- The app works on an IPv6-only NAT64 network, with no IPv4-only socket code or IP address literals, and the server answers over IPv6 if it publishes an AAAA record.
- Production backend, demo account and third-party services are live and not geo-blocked or rate-limited.
- No filler text, sample images, coming soon screens, beta badges, debug menus or staging banners remain.
- All in-app links and the Support, Marketing and Privacy Policy URLs open live pages.
- In-app purchases are added for review with this version, load in the sandbox and the paywall has a retry state.
- Build number incremented, build tested via TestFlight, review notes describe the fix and how to verify it.
Frequently asked questions
Apple says my app crashed, but I cannot reproduce it. What should I do?
Where do I find the crash log for a Guideline 2.1 rejection?
.ips file in Xcode or run xcrun crashlog on it, with the dSYMs of that build available. If nothing is attached, ask App Review for the log in your reply.Do I need to upload a new build after a 2.1 App Completeness rejection?
How do I test my iOS app on an IPv6-only network?
Why do my in-app purchases work in TestFlight but not during App Review?
Can I request an expedited review after fixing a 2.1 crash?
Is "Guideline 2.1 Information Needed" the same as App Completeness?
Official source: App Store Review Guidelines – 2.1 App Completeness. Store policies change regularly – always check the current version. This guide is independent advice and not affiliated with Apple or Google.