DEV Community

Lucas Martin
Lucas Martin

Posted on

Every EAS Submit Failure Mode I've Hit in Six Months (and What Catches Each Pre-Submit)

  • eas submit succeeding tells you the upload worked. It tells you nothing about whether the store will accept it
  • Six failures in six months, every one of them green in the terminal and red in App Store Connect or Play Console the next morning
  • Each one cost roughly a calendar day, because store review and processing don't run on your clock
  • All six are catchable before you run submit. The checks are at the bottom

I've submitted something like forty builds through EAS this year across a handful of Expo apps. The CLI has failed on me maybe twice. The store has bounced me six different ways, and in every case the terminal said "Submitted" and I went to bed.

That gap, green CLI and red store, is the whole story. eas submit validates that your credentials work and the binary uploaded. Everything else lives in App Store Connect or Play Console, and neither of those tells you anything until a human or a batch job gets to it, usually the next day.

Here's each failure in the order I hit them, and the pre-submit check that would have caught it.

1. Wrong ascAppId: the build went to the wrong app

I had two apps in the same Apple team, one production and one white-label. Both eas.json files were copy-pasted. Both had the same ascAppId.

{
  "submit": {
    "production": {
      "ios": {
        "appleId": "me@example.com",
        "ascAppId": "1234567890",
        "appleTeamId": "ABCDE12345"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The white-label build uploaded cleanly into the production app's TestFlight. Bundle identifier mismatch? Apple doesn't check that at upload time against ascAppId. It processed the build, attached it to the wrong app record, and I found out when a production tester asked why the icon had changed.

What catches it: compare ascAppId against the bundle identifier before submitting. The App Store Connect API gives you this in one call:

# returns bundleId for the app record ascAppId points at
curl -s -H "Authorization: Bearer $ASC_JWT" \
  "https://api.appstoreconnect.apple.com/v1/apps/$ASC_APP_ID" \
  | jq -r '.data.attributes.bundleId'
Enter fullscreen mode Exit fullscreen mode

If that doesn't equal ios.bundleIdentifier in app.json, stop. I run this as the first line of a presubmit.sh now.

2. The two things only App Store Connect knows about

Two separate failures, same root cause: the store needed something the binary can't carry.

In-app purchases not attached to the version. The IAP products existed. They were approved. They weren't attached to the app version I submitted for review, because that's a checkbox on the version page in App Store Connect, not anything in the build. The reviewer opened the paywall, tapped a product, got nothing, and rejected under 2.1.

Missing export compliance. The build sat in TestFlight with a yellow "Missing Compliance" badge and wouldn't distribute externally until someone answered the encryption question. That's a manual step in the TestFlight UI on every build, unless you declare it in the binary:

{
  "expo": {
    "ios": {
      "config": {
        "usesNonExemptEncryption": false
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

What catches it: usesNonExemptEncryption in app.json removes the compliance prompt permanently (assuming you're only using standard HTTPS, which is what the false means). For IAP, there's no config-side fix; it's a pre-submit checklist item: open the version in ASC, confirm every product is attached, then submit. Every time.

3. TestFlight external testers stopped getting builds

I had an external tester group of about thirty people. I submitted a new build, it processed, and nobody got it. The group hadn't vanished, but the build wasn't in it.

External groups don't automatically receive new builds unless "Automatic distribution" is on for the group, and each build still has to clear beta review first. I'd been manually adding builds to the group for months without realizing it, then forgot once.

What catches it: turn on automatic distribution for the group in TestFlight, once. Or, if you want it explicit, add the build to the group from the pipeline after submit using the ASC API's betaGroups/{id}/relationships/builds endpoint. Either way, don't rely on remembering.

4. Play Console blocked the release on Data Safety

The AAB uploaded. The release page showed one error: "Your Data safety section is incomplete." That section is a questionnaire about every category of data the app collects, and it has to be completed before the first production release. It's also easy to invalidate: add an analytics SDK, change your answers, or the next release is blocked again.

There are four of these gates on the Play side and none of them are in the build: Data Safety, Content Rating questionnaire, Target Audience, and the Ads declaration. Miss any one and the release button is disabled.

What catches it: open the Policy → App content page before you build, not after. Everything on that page with a "Start" or "Update" button is going to block you. New Play Console accounts also have to run a 14-day closed test with at least 12 testers before production access is granted, which isn't a submit failure so much as a submit-two-weeks-later.

5. Android versionCode already used

Version code 42 has already been used. Try another version code.
Enter fullscreen mode Exit fullscreen mode

This one is the most CLI-adjacent of the six, and it still comes from the store, not from EAS. It happened because versionCode was set in app.json and I'd built twice without bumping it. It also happens when a build goes to internal testing and then a rebuild goes to production with the same code.

What catches it: let EAS own the number.

{
  "cli": { "appVersionSource": "remote" },
  "build": {
    "production": { "autoIncrement": true }
  }
}
Enter fullscreen mode Exit fullscreen mode

With appVersionSource: remote, the version lives on EAS servers and autoIncrement bumps it per build. eas build:version:get shows you the current value if you want to sanity-check it against Play Console before submitting.

6. iOS screenshots at the wrong pixel size

App Store Connect rejects screenshot uploads whose dimensions don't match one of Apple's exact device sizes. Not "roughly a phone." Exact. My designer exported at 1284×2778 (an older 6.5" size) when the required set for the listing was 6.9" at 1320×2868. The submission was blocked on the version page until every slot was filled with the right size.

What catches it: check dimensions before uploading. On macOS:

for f in screenshots/ios/*.png; do
  sips -g pixelWidth -g pixelHeight "$f" | awk -v f="$f" 'NR>1{printf "%s ", $2} END{print f}'
done
Enter fullscreen mode Exit fullscreen mode

Then compare the output against Apple's current screenshot specifications for the device sizes you're targeting. The required sizes change when new devices ship, so don't hardcode them; link to the spec page in the checklist.

The pattern

Green CLI, red store, one day each. Six days of calendar time lost to things that took under an hour to fix once I knew what they were. None of them are Expo's fault. eas submit does exactly what it says. The failures all live in the layer between the upload and the review, which nothing in the codebase can see.

The pre-submit checklist that came out of this:

  1. ascAppId resolves to the same bundle ID as app.json
  2. usesNonExemptEncryption set; IAP products attached to the version in ASC
  3. TestFlight external groups on automatic distribution
  4. Play Console App content page has zero pending items
  5. appVersionSource: remote and autoIncrement on
  6. Screenshot dimensions verified against Apple's current spec

Steps 1, 2 (compliance), 5, and 6 are scriptable. Steps 2 (IAP), 3, and 4 are a human opening a browser tab, which is exactly why they get skipped.

That human-in-the-browser layer is what LetsDeployIt exists for: they take Expo apps through the store side, with a person checking each of these gates, so the submit you run at 11pm is the last one. If you'd rather own it yourself, the six checks above are the whole list, at least until I hit number seven.

What's the one that got you? I've heard rumors about Play's pre-launch report flagging a crash in the sample app that isn't reachable from the real UI, but I haven't seen it myself yet.

Top comments (0)