An eas submit step works on your laptop but fails in a non-interactive CI job; what must the EAS submit profile and environment provide for iOS and Android?
answer
- nothing may prompt
- EXPO_TOKEN for the Expo account
- archive source flag required
- ascAppId plus an App Store Connect API key
- service account key, first Play upload manual
basics
~20 sEvery prompt must be answered in advance: EXPO_TOKEN for the Expo account, an explicit archive source, and on iOS an ascAppId plus an App Store Connect API key; on Android a Google Service Account key and one earlier manual upload.
solid answer
~40 sOn a laptop `eas submit` fills gaps interactively: it lists builds, logs in to Apple and looks the app up. With `--non-interactive` every gap is an error. The job needs `EXPO_TOKEN` for the Expo account and an archive source (`--latest`, `--id`, `--path` or `--url`). For iOS, `ascAppId` must be in the submit profile, and App Store Connect authentication must come from an **API key**: one stored in EAS credentials, or `ascApiKeyPath` plus `ascApiKeyId` (and `ascApiKeyIssuerId` for a team key) in the profile, which can read `$VAR` values from the environment. Setting only some of those three fails. For Android, the Google Service Account key comes from EAS credentials or `serviceAccountKeyPath`, and the app must have been uploaded manually once.
code
json · 16 lines{
"submit": {
"production": {
"ios": {
"ascAppId": "1234567890",
"ascApiKeyPath": "$ASC_API_KEY_PATH",
"ascApiKeyId": "$ASC_API_KEY_ID",
"ascApiKeyIssuerId": "$ASC_API_KEY_ISSUER_ID"
},
"android": {
"serviceAccountKeyPath": "$GOOGLE_SERVICE_ACCOUNT",
"track": "internal"
}
}
}
}go deeper
Recall that CI needs EXPO_TOKEN and cannot answer prompts, so the command must name the build to upload.
Explain which prompts become errors in non-interactive mode: archive source, ascAppId and App Store Connect credentials.
Walk the credential resolution order, keep keys in CI secrets via $VAR fields, and recognise the manual first Play upload as a non-credential failure.
Decide who owns store credentials: team API keys in EAS or in the CI secret store, rotation, and how many people can release unattended.
## Why the laptop run succeeds Run interactively, `eas submit` is forgiving. If you do not say which build to upload, it shows a list. If the iOS profile has no `ascAppId`, it signs in to your Apple account and finds or creates the app record in **App Store Connect**. If credentials are missing, it asks. Every one of those steps needs a person, so in a CI job — which runs with `--non-interactive` — each becomes a failure. The fix is to answer every question in configuration. ## What every CI submission needs 1. **Expo authentication.** CI cannot run `eas login`; it provides a personal access token in the `EXPO_TOKEN` environment variable. 2. **An archive source.** Without `--latest`, `--id`, `--path` or `--url`, the CLI stops with "You need to specify the archive source when running in non-interactive mode". In a pipeline, `--id` with the ID from the build step is the most precise. 3. **A platform.** Pass `--platform ios` or `--platform android` so nothing asks; `--id`, `--path` and `--url` only work with a single platform anyway. ## iOS: app identity and App Store Connect authentication The iOS profile must carry **`ascAppId`**, the numeric Apple ID of the app record. Without it the CLI fails in non-interactive mode with "Set ascAppId in the submit profile (eas.json) or re-run this command in interactive mode." For authentication, EAS CLI resolves credentials in this order: | Condition | What EAS Submit uses | |---|---| | `EXPO_APPLE_APP_SPECIFIC_PASSWORD` is set | an Apple ID app-specific password, with `appleId` in the profile | | `ascApiKeyPath` and `ascApiKeyId` are both set | that `.p8` key file; `ascApiKeyIssuerId` too unless it is an individual key | | only some of the three `ascApiKey*` fields are set | an error in non-interactive mode | | none of them is set | an App Store Connect API key stored in the project's EAS credentials | The **API key** is the recommended path: no human Apple ID, no two-factor prompt. You can upload one once with `eas credentials` (the "App Store Connect: Manage your API Key" menu) and leave the profile without key fields, or keep the key yourself and reference it. The three `ascApiKey*` fields are **evaluated against the environment**, so a profile can hold `"ascApiKeyPath": "$ASC_API_KEY_PATH"` and the CI secret store supplies the value. That keeps the `.p8` file and its IDs out of the repository. The schema also checks the resolved values: `ascApiKeyId` must be uppercase letters and digits, `ascApiKeyIssuerId` a UUID, and `ascAppId` digits only. ## Android: service account and the first upload Google Play accepts API uploads authenticated by a **Google Service Account** JSON key with access to the app. EAS Submit takes it from `serviceAccountKeyPath` in the profile, which is also environment-evaluated, so `"$GOOGLE_SERVICE_ACCOUNT"` works, or, when the path is absent, from a key uploaded to the project's EAS credentials. Two Android failures look like authentication problems but are not: - **First release.** Google's API cannot create an app's first release, so a brand-new app needs one manual upload in the Play Console. Until then every automated submit fails, however good the key is. - **Package name.** If the project cannot resolve one `applicationId` unambiguously, set `applicationId` in the submit profile. ## Where the credentials should live There are two defensible homes for store credentials. Storing the App Store Connect API key and the service account key in **EAS credentials** keeps the profile free of key fields and lets any authorised machine submit with only `EXPO_TOKEN`. Keeping them in the **CI secret store** and referencing them through `$VAR` fields keeps the keys wherever the rest of the pipeline's secrets are managed. Either works; mixing them without knowing which one wins is what produces "works here, fails there" reports. ## A diagnosis checklist When a submit job fails only in CI, check in this order: - Is `EXPO_TOKEN` present in the job's environment? - Does the command name an archive source and a single platform? - iOS: is `ascAppId` in the resolved profile, and are the `ascApiKey*` fields either all needed ones set or all absent? - Do `$VAR` references resolve, meaning the secret is exposed to that step? - Android: has the app been uploaded manually once, and does the service account have access to it? The unifying idea is that a laptop run is a conversation, while a CI run has to be a complete letter.
- Why prefer an App Store Connect API key over an app-specific password for CI submissions?The API key is a team credential meant for automation: no personal Apple ID, no two-factor prompt, and it can be stored in EAS credentials. The app-specific password path, `EXPO_APPLE_APP_SPECIFIC_PASSWORD` with `appleId`, ties the pipeline to one person's account.
- A profile sets ascApiKeyPath and ascApiKeyIssuerId but not ascApiKeyId; what does eas submit do in CI?It treats the partial set as an intent to use a key file it cannot complete. Interactively it warns and prompts; with `--non-interactive` it throws, saying `ascApiKeyPath` and `ascApiKeyId` must both be defined, with the issuer ID also needed unless the key is an individual one.
- How does a submit profile keep the .p8 key path and IDs out of eas.json?EAS CLI evaluates `ascApiKeyPath`, `ascApiKeyId`, `ascApiKeyIssuerId` and Android's `serviceAccountKeyPath` against the environment, so values such as `$ASC_API_KEY_PATH` are filled from the CI job's secrets when the profile is resolved.
saying these in an interview costs you the question
- CI can run eas login with a password prompt like a laptop
- Without an archive flag, eas submit picks the latest build in CI
- ascAppId is optional because the CLI can always look the app up
- Setting just ascApiKeyPath is enough for API-key authentication
- A valid service account key lets you submit a brand-new app's first release